Want the code? Check out VENSnowOverlayView on Github

I wanted to find a way to make Venmo a little festive on Christmas day for the team and thought it’d be fun to overlay snow on the app. A couple hours later (and with liberal copying from Felice’s work) here’s the result:


snow_side snow_new

 

Making it snow

The approach is simple, take an image of a snowflake, create lots of them at randomly scaled sizes and then use CABasicAnimations to animate the image layers down and add some rotation for prettiness.

for (int i=0; i < self.flakesCount; i++) {
    // Randomize Flake size
    float flakeScale = ((Float32)1.0*(Float32)random()/(Float32)RAND_MAX);
    ....
    UIImageView *imageView = [[UIImageView alloc] initWithImage:flakeImg];
    imageView.frame        = frame;

    imageView.userInteractionEnabled = NO;
    [self.flakesArray addObject:imageView];
    [self addSubview:imageView];
}

Then when you want to actually start your snowing animation:

// Prepare Rotation Animation
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
rotationAnimation.repeatCount       = 1e100;
rotationAnimation.autoreverses      = NO;
rotationAnimation.toValue = [NSNumber numberWithFloat:6.28318531];	// 360 degrees in radians

// Prepare Vertical Motion Animation
CABasicAnimation *fallAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
fallAnimation.repeatCount       = 1e100;
fallAnimation.autoreverses      = NO;

for (UIImageView *flake in self.flakesArray) {
    float timeInterval       = ((Float32)(self.animationDurationMax - self.animationDurationMin) * (Float32)random() / (Float32)RAND_MAX);
    fallAnimation.duration   = timeInterval + self.animationDurationMin;
    fallAnimation.fromValue  = [NSNumber numberWithFloat:-flakeStartY];
    [flake.layer addAnimation:fallAnimation forKey:@"transform.translation.y"];

    rotationAnimation.duration = timeInterval * 2; // Makes sure that we don't get super-fast spinning flakes
    [flake.layer addAnimation:rotationAnimation forKey:@"transform.rotation.y"];
}

These CABasicAnimations will then run constantly and ensure that there’s always good ‘snow coverage’ of your view.

Edge Cases

As you may know, CABasicAnimations can behave very oddly when your app is in the background — most notably in our case, animations complete but don’t repeat meaning that we end up with all the snowflakes at the bottom of the screen! We need to make sure that we kill and restart our snowing overlay when the app looses focus:

- (void)beginSnowAnimation {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(endSnowAnimationFromNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
    ...
    // Actually begin the animation
}

- (void)endSnowAnimationFromNotification:(NSNotification *)notification {
    [self endSnowAnimation];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginSnowAnimation) name:UIApplicationDidBecomeActiveNotification object:nil];
}

 Enjoy

Feel free to use and extend this code. I’m submitting a pod to the CocoaPods spec repo later today so you should be able to easily install it!

Want the code? Check out VENSnowOverlayView on Github