├── .gitignore ├── Icon.png ├── Snoverlay.plist ├── snoverlayprefs ├── Resources │ ├── Snoverlay.png │ ├── Snoverlay@2x.png │ ├── Snoverlay@3x.png │ ├── Info.plist │ └── Root.plist ├── SNOWRootListController.h ├── SNOWRootListController.m ├── Makefile └── entry.plist ├── layout └── Library │ └── Application Support │ └── Snoverlay │ ├── XMASSnowflake.png │ └── XMASSnowflake1.png ├── control ├── FallingSnow ├── XMASFallingSnowView.h └── XMASFallingSnowView.m ├── Makefile ├── CODE_OF_CONDUCT.md └── Tweak.xm /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .theos -------------------------------------------------------------------------------- /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryannair05/Snoverlay-2/HEAD/Icon.png -------------------------------------------------------------------------------- /Snoverlay.plist: -------------------------------------------------------------------------------- 1 | { 2 | Filter = { 3 | Bundles = ( 4 | "com.apple.springboard", 5 | "com.apple.CarPlayApp", 6 | ); 7 | }; 8 | } -------------------------------------------------------------------------------- /snoverlayprefs/Resources/Snoverlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryannair05/Snoverlay-2/HEAD/snoverlayprefs/Resources/Snoverlay.png -------------------------------------------------------------------------------- /snoverlayprefs/Resources/Snoverlay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryannair05/Snoverlay-2/HEAD/snoverlayprefs/Resources/Snoverlay@2x.png -------------------------------------------------------------------------------- /snoverlayprefs/Resources/Snoverlay@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryannair05/Snoverlay-2/HEAD/snoverlayprefs/Resources/Snoverlay@3x.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Snoverlay/XMASSnowflake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryannair05/Snoverlay-2/HEAD/layout/Library/Application Support/Snoverlay/XMASSnowflake.png -------------------------------------------------------------------------------- /layout/Library/Application Support/Snoverlay/XMASSnowflake1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryannair05/Snoverlay-2/HEAD/layout/Library/Application Support/Snoverlay/XMASSnowflake1.png -------------------------------------------------------------------------------- /snoverlayprefs/SNOWRootListController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface SNOWRootListController : PSListController 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.ryannair05.snoverlay 2 | Name: Snoverlay 2 3 | Depends: mobilesubstrate, preferenceloader, firmware (>= 7.0) 4 | Version: 1.0.1 5 | Architecture: iphoneos-arm 6 | Description: Falling Snowflakes 7 | Maintainer: Ryan Nair 8 | Author: Ryan Nair 9 | Section: Tweaks 10 | Replaces: com.leftyfl1p.snoverlay -------------------------------------------------------------------------------- /snoverlayprefs/SNOWRootListController.m: -------------------------------------------------------------------------------- 1 | #include "SNOWRootListController.h" 2 | 3 | @implementation SNOWRootListController 4 | 5 | - (NSArray *)specifiers { 6 | if (!_specifiers) { 7 | _specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self]; 8 | } 9 | 10 | return _specifiers; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /snoverlayprefs/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = armv7 arm64 arm64e 2 | include $(THEOS)/makefiles/common.mk 3 | 4 | BUNDLE_NAME = snoverlayprefs 5 | snoverlayprefs_FILES = SNOWRootListController.m 6 | snoverlayprefs_INSTALL_PATH = /Library/PreferenceBundles 7 | snoverlayprefs_PRIVATE_FRAMEWORKS = Preferences 8 | 9 | include $(THEOS_MAKE_PATH)/bundle.mk 10 | 11 | internal-stage:: 12 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 13 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/snoverlayprefs.plist$(ECHO_END) 14 | -------------------------------------------------------------------------------- /FallingSnow/XMASFallingSnowView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface XMASFallingSnowView : UIView { 6 | CAEmitterLayer *_snowEmitterLayer; 7 | } 8 | 9 | @property (nonatomic, retain) NSMutableArray *flakesArray; 10 | @property (nonatomic, retain) NSString *flakeFileName; 11 | @property (nonatomic, assign) NSInteger flakesCount; 12 | @property (nonatomic, assign) float animationDurationMin; 13 | @property (nonatomic, assign) float animationDurationMax; 14 | 15 | - (void)beginSnowAnimation; 16 | - (void)beginSnowEmmitter; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | THEOS_DEVICE_IP = 192.168.1.237 2 | 3 | export PREFIX = $(THEOS)/toolchain/Xcode.xctoolchain/usr/bin/ 4 | 5 | FINALPACKAGE=1 6 | 7 | export TARGET = iphone:13.5:7.0 8 | 9 | export ADDITIONAL_CFLAGS = -DTHEOS_LEAN_AND_MEAN -fobjc-arc -O3 10 | 11 | include $(THEOS)/makefiles/common.mk 12 | 13 | TWEAK_NAME = Snoverlay 14 | Snoverlay_FILES = Tweak.xm ./FallingSnow/XMASFallingSnowView.m 15 | 16 | ARCHS = armv7 arm64 arm64e 17 | 18 | include $(THEOS_MAKE_PATH)/tweak.mk 19 | 20 | after-install:: 21 | install.exec "killall -9 SpringBoard" 22 | SUBPROJECTS += snoverlayprefs 23 | include $(THEOS_MAKE_PATH)/aggregate.mk 24 | -------------------------------------------------------------------------------- /snoverlayprefs/entry.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | snoverlayprefs 9 | cell 10 | PSLinkCell 11 | detail 12 | SNOWRootListController 13 | icon 14 | Snoverlay.png 15 | isController 16 | 17 | label 18 | Snoverlay 2 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /snoverlayprefs/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | snoverlayprefs 9 | CFBundleIdentifier 10 | com.ryannair05.snoverlayprefs 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSPrincipalClass 22 | SNOWRootListController 23 | 24 | 25 | -------------------------------------------------------------------------------- /snoverlayprefs/Resources/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | label 11 | Snoverlay 2 12 | footerText 13 | Higher quality increases battery drain, but gives the snowflakes nicer animations 14 | 15 | 16 | cell 17 | PSSwitchCell 18 | default 19 | 20 | defaults 21 | com.ryannair05.snoverlay 22 | PostNotification 23 | com.ryannair05.snoverlay/prefsupdated 24 | key 25 | enabled 26 | label 27 | Enable 28 | 29 | 30 | cell 31 | PSSwitchCell 32 | default 33 | 34 | defaults 35 | com.ryannair05.snoverlay 36 | PostNotification 37 | com.ryannair05.snoverlay/prefsupdated 38 | key 39 | wallpaperOnly 40 | label 41 | Wallpaper Only 42 | 43 | 44 | cell 45 | PSSwitchCell 46 | default 47 | 48 | defaults 49 | com.ryannair05.snoverlay 50 | PostNotification 51 | com.ryannair05.snoverlay/prefsupdated 52 | key 53 | highQuality 54 | label 55 | High Quality 56 | 57 | 58 | cell 59 | PSGroupCell 60 | label 61 | Number of Snowflakes 62 | isStaticText 63 | 64 | 65 | 66 | PostNotification 67 | com.ryannair05.snoverlay/prefsupdated 68 | cell 69 | PSSliderCell 70 | default 71 | 160 72 | defaults 73 | com.ryannair05.snoverlay 74 | key 75 | numSnowflakes 76 | label 77 | Number of Snowflakes 78 | max 79 | 200 80 | min 81 | 15 82 | showValue 83 | 84 | 85 | 86 | cell 87 | PSGroupCell 88 | label 89 | Snowflake Style 90 | isStaticText 91 | 92 | 93 | 94 | cell 95 | PSSegmentCell 96 | default 97 | 0 98 | defaults 99 | com.ryannair05.snoverlay 100 | key 101 | snowFlakeType 102 | validValues 103 | 104 | 0 105 | 1 106 | 107 | validTitles 108 | 109 | White 110 | Blue 111 | 112 | alignment 113 | 2 114 | PostNotification 115 | com.ryannair05.snoverlay/prefsupdated 116 | 117 | 118 | title 119 | Snoverlay 2 120 | 121 | 122 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Citizen Code of Conduct 2 | 3 | ## 1. Purpose 4 | 5 | A primary goal of Snoverlay is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all. 6 | 7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. 8 | 9 | We invite all those who participate in Snoverlay to help us create safe and positive experiences for everyone. 10 | 11 | ## 2. Open [Source/Culture/Tech] Citizenship 12 | 13 | A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. 14 | 15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. 16 | 17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. 18 | 19 | ## 3. Expected Behavior 20 | 21 | The following behaviors are expected and requested of all community members: 22 | 23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. 24 | * Exercise consideration and respect in your speech and actions. 25 | * Attempt collaboration before conflict. 26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech. 27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. 28 | 29 | ## 4. Reporting Guidelines 30 | 31 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer [here](mailto:ryannair05@gmail.com?subject=Reporting%20Guidelinesgmail.com/?subject=Reporting%20Guidelines). 32 | 33 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. 34 | 35 | ## 5. Addressing Grievances 36 | 37 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. 38 | 39 | 40 | ## 6. Scope 41 | 42 | We expect all community participants (contributors, paid or otherwise, sponsors and other guests) to abide by this Code of Conduct in all community venues--online and in-person--as well as in all one-on-one communications pertaining to community business. 43 | 44 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. 45 | 46 | ## 7. Contact info 47 | 48 | ryannair05@gmail.com 49 | 50 | ## 8. License and attribution 51 | 52 | The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org) under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). 53 | 54 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). 55 | 56 | _Revision 2.3. Posted 6 March 2017._ 57 | 58 | _Revision 2.2. Posted 4 February 2016._ 59 | 60 | _Revision 2.1. Posted 23 June 2014._ 61 | 62 | _Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10 January 2013. Posted 17 March 2013._ 63 | -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | #import "FallingSnow/XMASFallingSnowView.h" 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | @interface SnoverlaySecureWindow : UIWindow 8 | @property (nonatomic, retain) XMASFallingSnowView* snowView; 9 | @end 10 | 11 | @implementation SnoverlaySecureWindow 12 | 13 | -(id)initWithFrame:(CGRect)frame { 14 | if(self = [super initWithFrame:frame]) 15 | { 16 | 17 | Boolean found; 18 | 19 | if (CFPreferencesGetAppBooleanValue(CFSTR("wallpaperOnly"), CFSTR("com.ryannair05.snoverlay"), &found) == false && found) { 20 | self.snowView = [[XMASFallingSnowView alloc] initWithFrame:self.frame]; 21 | [self addSubview:self.snowView]; 22 | } 23 | 24 | [[NSNotificationCenter defaultCenter] addObserverForName:@"com.ryannair05.snoverlay/prefsupdated" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { 25 | Boolean found; 26 | 27 | [self.snowView removeFromSuperview]; 28 | self.snowView = nil; 29 | 30 | if (CFPreferencesGetAppBooleanValue(CFSTR("wallpaperOnly"), CFSTR("com.ryannair05.snoverlay"), &found) == false && found) { 31 | self.snowView = [[XMASFallingSnowView alloc] initWithFrame:self.frame]; 32 | [self addSubview:self.snowView]; 33 | } 34 | }]; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | -(void)dealloc { 41 | [[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.ryannair05.snoverlay/prefsupdated"object:nil]; 42 | } 43 | 44 | - (BOOL)_shouldCreateContextAsSecure { 45 | return YES; 46 | } 47 | @end 48 | 49 | @interface SBFStaticWallpaperView : UIView 50 | @property (nonatomic, retain) XMASFallingSnowView* snowView; 51 | @end 52 | 53 | static void SBFStaticWallpaperView_handlePrefs(__unsafe_unretained SBFStaticWallpaperView* const self) { 54 | self.snowView = [[XMASFallingSnowView alloc] initWithFrame:self.frame]; 55 | [self addSubview:self.snowView]; 56 | 57 | self.snowView.translatesAutoresizingMaskIntoConstraints = NO; 58 | [self.snowView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor].active = YES; 59 | [self.snowView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor].active = YES; 60 | [self.snowView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES; 61 | [self.snowView.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES; 62 | } 63 | 64 | %hook SBFStaticWallpaperView 65 | %property (nonatomic, retain) XMASFallingSnowView* snowView; 66 | -(void)_setupContentViewWithOptions:(NSUInteger)options { 67 | 68 | %orig; 69 | 70 | SBFStaticWallpaperView_handlePrefs(self); 71 | } 72 | 73 | -(void)_setupContentView { 74 | %orig; 75 | 76 | SBFStaticWallpaperView_handlePrefs(self); 77 | } 78 | 79 | %end 80 | 81 | %hook CarplayLockOutViewController 82 | %property (nonatomic, retain) XMASFallingSnowView* snowView; 83 | -(void)viewDidAppear:(BOOL)arg1 { 84 | %orig; 85 | 86 | [self setSnowView:[[XMASFallingSnowView alloc] initWithFrame:[self view].frame]]; 87 | [[self view] addSubview:[self snowView]]; 88 | } 89 | %end 90 | 91 | %hook SpringBoard 92 | -(void)applicationDidFinishLaunching:(id)application { 93 | %orig; 94 | CGRect frame = [UIScreen mainScreen].bounds; 95 | SnoverlaySecureWindow *window = [[SnoverlaySecureWindow alloc] initWithFrame:frame]; 96 | window.windowLevel = 100000.0f; 97 | window.userInteractionEnabled = NO; 98 | window.hidden = NO; 99 | } 100 | %end 101 | 102 | static void loadPrefs() 103 | { 104 | [[NSNotificationCenter defaultCenter] postNotificationName:@"com.ryannair05.snoverlay/prefsupdated" object:nil]; 105 | } 106 | 107 | %ctor { 108 | @autoreleasepool { 109 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL,(CFNotificationCallback)loadPrefs, CFSTR("com.ryannair05.snoverlay/prefsupdated"), NULL, CFNotificationSuspensionBehaviorCoalesce); 110 | 111 | %init(_ungrouped, CarplayLockOutViewController = objc_getClass(kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_13_0 ? "CARLockOutViewController" : "SBStarkLockOutViewController")) 112 | } 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /FallingSnow/XMASFallingSnowView.m: -------------------------------------------------------------------------------- 1 | #import "XMASFallingSnowView.h" 2 | #import 3 | 4 | #define selfFlakeWidth 20.0 5 | #define selfFlakeHeight 23.0 6 | #define flakeMinimumSize 0.4 7 | 8 | @implementation XMASFallingSnowView 9 | 10 | - (id)initWithFrame:(CGRect)frame { 11 | if ((self = [super initWithFrame:frame])) { 12 | self.animationDurationMin = 5; 13 | self.animationDurationMax = 11; 14 | 15 | NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.ryannair05.snoverlay"]; 16 | 17 | [userDefaults registerDefaults:@{ 18 | @"enabled": @YES, 19 | @"numSnowflakes": @160, 20 | @"snowFlakeType": @0, 21 | @"highQuality" : @NO 22 | }]; 23 | 24 | if ([userDefaults boolForKey:@"enabled"]) { 25 | self.flakeFileName = [userDefaults integerForKey:@"snowFlakeType"] ? @"XMASSnowflake1.png" : @"XMASSnowflake.png"; 26 | self.flakesCount = [userDefaults integerForKey:@"numSnowflakes"]; 27 | 28 | if ([userDefaults boolForKey:@"highQuality"]) { 29 | [self performSelector:@selector(beginSnowAnimation) withObject:nil afterDelay:1.5]; 30 | } 31 | else { 32 | [self beginSnowEmmitter]; 33 | } 34 | } 35 | } 36 | 37 | [[NSNotificationCenter defaultCenter] addObserver:self 38 | selector:@selector(handlePrefs) 39 | name:@"com.ryannair05.snoverlay/prefsupdated" 40 | object:nil]; 41 | 42 | return self; 43 | } 44 | 45 | -(void)dealloc { 46 | [[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.ryannair05.snoverlay/prefsupdated"object:nil]; 47 | } 48 | 49 | -(void)handlePrefs { 50 | NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.ryannair05.snoverlay"]; 51 | 52 | [userDefaults registerDefaults:@{ 53 | @"enabled": @YES, 54 | @"numSnowflakes": @160, 55 | @"snowFlakeType": @0, 56 | @"highQuality" : @NO 57 | }]; 58 | 59 | [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)]; 60 | self.layer.sublayers = nil; 61 | self.flakesArray = nil; 62 | 63 | if ([userDefaults boolForKey:@"enabled"]) { 64 | self.flakeFileName = [userDefaults integerForKey:@"snowFlakeType"] ? @"XMASSnowflake1.png" : @"XMASSnowflake.png"; 65 | self.flakesCount = [userDefaults integerForKey:@"numSnowflakes"]; 66 | 67 | if ([userDefaults boolForKey:@"highQuality"]) { 68 | [self beginSnowAnimation]; 69 | } 70 | else { 71 | [self beginSnowEmmitter]; 72 | } 73 | } 74 | } 75 | 76 | - (void)beginSnowEmmitter { 77 | UIImage *flakeImg = [UIImage imageWithContentsOfFile: [@"/Library/Application Support/Snoverlay/" stringByAppendingString: self.flakeFileName]]; 78 | 79 | CAEmitterCell *flakeEmitterCell = [CAEmitterCell emitterCell]; 80 | flakeEmitterCell.contents = (__bridge id _Nullable) flakeImg.CGImage; 81 | if ([self.flakeFileName isEqualToString:@"XMASSnowflake.png"]) { 82 | flakeEmitterCell.scale = 0.3; 83 | flakeEmitterCell.scaleRange = 0.25; 84 | } 85 | else { 86 | flakeEmitterCell.scale = 0.05; 87 | flakeEmitterCell.scaleRange = 0.03; 88 | } 89 | flakeEmitterCell.lifetime = 15.0; 90 | flakeEmitterCell.birthRate = self.flakesCount >> 3; 91 | flakeEmitterCell.emissionRange = M_PI; 92 | flakeEmitterCell.velocity = -20; 93 | flakeEmitterCell.velocityRange = 100; 94 | flakeEmitterCell.yAcceleration = 20; 95 | flakeEmitterCell.zAcceleration = 10; 96 | flakeEmitterCell.xAcceleration = 5; 97 | flakeEmitterCell.spinRange = M_PI * 2; 98 | 99 | _snowEmitterLayer = [CAEmitterLayer layer]; 100 | _snowEmitterLayer.emitterPosition = CGPointMake(self.bounds.size.width / 2.0 - 50, -50); 101 | _snowEmitterLayer.emitterSize = CGSizeMake(self.bounds.size.width, 0); 102 | _snowEmitterLayer.emitterShape = kCAEmitterLayerLine; 103 | _snowEmitterLayer.beginTime = CACurrentMediaTime(); 104 | _snowEmitterLayer.timeOffset = 10; 105 | _snowEmitterLayer.emitterCells = [NSArray arrayWithObject:flakeEmitterCell]; 106 | 107 | [self.layer addSublayer:_snowEmitterLayer]; 108 | } 109 | 110 | -(void)layoutSubviews{ 111 | [super layoutSubviews]; 112 | if (_snowEmitterLayer) { 113 | _snowEmitterLayer.emitterPosition = CGPointMake(self.center.x, -50); 114 | } 115 | else { 116 | UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; 117 | if (UIDeviceOrientationIsLandscape(orientation)) { 118 | self.transform = CGAffineTransformMakeRotation(M_PI * 1.5); 119 | 120 | } 121 | else { 122 | self.transform = CGAffineTransformMakeRotation(0); 123 | } 124 | 125 | self.frame = CGRectOffset(self.frame, 0.0, [UIScreen mainScreen].bounds.size.height - [UIScreen mainScreen].bounds.size.width); 126 | } 127 | } 128 | 129 | - (void)beginSnowAnimation { 130 | // Clean up if we go to the background as CABasicAnimations tend to do odd things then 131 | 132 | // Prepare Rotation Animation 133 | CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; 134 | rotationAnimation.repeatCount = HUGE_VALF; 135 | rotationAnimation.autoreverses = NO; 136 | rotationAnimation.toValue = [NSNumber numberWithDouble:M_PI * 2]; // 360 degrees in radians 137 | 138 | // Prepare Vertical Motion Animation 139 | CABasicAnimation *fallAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"]; 140 | fallAnimation.repeatCount = HUGE_VALF; 141 | fallAnimation.autoreverses = NO; 142 | 143 | for (UIImageView *flake in self.flakesArray) { 144 | CGPoint flakeStartPoint = flake.center; 145 | double flakeStartY = flakeStartPoint.y; 146 | double flakeEndY = self.frame.size.height; 147 | flakeStartPoint.y = flakeEndY; 148 | flake.center = flakeStartPoint; 149 | 150 | // Randomize the time each flake takes to animate to give texture 151 | double timeInterval = (self.animationDurationMax - self.animationDurationMin) * arc4random() / UINT32_MAX; 152 | fallAnimation.duration = timeInterval + self.animationDurationMin; 153 | fallAnimation.fromValue = [NSNumber numberWithDouble:-flakeStartY]; 154 | [flake.layer addAnimation:fallAnimation forKey:@"transform.translation.y"]; 155 | 156 | rotationAnimation.duration = timeInterval * 2; // Makes sure that we don't get super-fast spinning flakes 157 | [flake.layer addAnimation:rotationAnimation forKey:@"transform.rotation.y"]; 158 | } 159 | } 160 | 161 | -(NSMutableArray *)flakesArray { 162 | if (!_flakesArray) { 163 | srandomdev(); 164 | self.flakesArray = [[NSMutableArray alloc] initWithCapacity:self.flakesCount]; 165 | // UIImage *flakeImg = [UIImage imageNamed:self.flakeFileName]; 166 | // I'm not really fond of the practice of putting files in a system app directory, even if it is technically harmless :/ 167 | UIImage *flakeImg = [UIImage imageWithContentsOfFile: [@"/Library/Application Support/Snoverlay/" stringByAppendingString: self.flakeFileName]]; 168 | 169 | double flakeXPosition, flakeYPosition; 170 | 171 | for (int i = 0; i < self.flakesCount; i++) { 172 | // Randomize Flake size 173 | double flakeScale = ((double)arc4random() / UINT32_MAX); 174 | 175 | // Make sure that we don't break the current size rules 176 | flakeScale = flakeScale < flakeMinimumSize ? flakeMinimumSize : flakeScale; 177 | double flakeWidth = selfFlakeWidth * flakeScale; 178 | double flakeHeight = selfFlakeHeight * flakeScale; 179 | 180 | // Allow flakes to be partially offscreen 181 | flakeXPosition = self.frame.size.width * arc4random() / UINT32_MAX; 182 | flakeXPosition -= flakeWidth; 183 | 184 | // enlarge content height by 1/2 view height, screen is always well populated 185 | flakeYPosition = self.frame.size.height * 1.5 * arc4random() / UINT32_MAX; 186 | // flakes start y position is above upper view bound, add view height 187 | flakeYPosition += self.frame.size.height; 188 | 189 | CGRect frame = CGRectMake(flakeXPosition, flakeYPosition, flakeWidth, flakeHeight); 190 | 191 | UIImageView *imageView = [[UIImageView alloc] initWithImage:flakeImg]; 192 | imageView.frame = frame; 193 | imageView.userInteractionEnabled = NO; 194 | 195 | [self.flakesArray addObject:imageView]; 196 | [self addSubview:imageView]; 197 | } 198 | } 199 | return _flakesArray; 200 | } 201 | 202 | @end 203 | --------------------------------------------------------------------------------