Episode #104

Blur

16 minutes
Published on January 23, 2014

This video is only available to subscribers. Get access to this video and 573 others.

In this episode we tackle how to implement an ever increasing trend in iOS 7 app design: blur. We start by doing it the "easy" way using UIToolbar, then move on to a more custom and flexible approach using our own blurred image.

Episode Links

  • Episode Source Code
  • Apple's UIImage+ImageEffect - Sample code from WWDC that uses vImage to produce a heavily saturated dark or light blur. You'll have to sign in with your Apple Developer credentials to download this.
  • FXBlurView - Provides much more flexibility than what I demonstrate in the screencast.

Blurring with UIToolbar

The easiest method to do blurring is with a UIToolbar. Toolbars are just views and they have a specific style in iOS 7 that offers the blur behavior we're looking for. By setting a toolbar as our panel's background we get the effect we're after.

Note that the barStyle and translucent properties are the only real requirement here.

   UIToolbar *blurToolbar = [[UIToolbar alloc] initWithFrame:self.panel.bounds];
    blurToolbar.barStyle = UIBarStyleBlackTranslucent;
    blurToolbar.translucent = YES;
    blurToolbar.alpha = 0.94;
    [self.panel addSubview:blurToolbar];

While easy, this approach has some drawbacks. You might not want to depend on UIToolbar always looking this way (for instance, think how ridiculous this would look in iOS 6 with the same class). In addition it really is bending UIToolbar beyond what it was meant for.

Instead you might choose to do it by yourself, which proves not too difficult (with a bit of help from Apple).

Blurring with cached images

The first step is caching a blurred image containing all of the elements in the view hierarchy. This can be accomplished with iOS 7's new drawViewHierarchyInRect:afterScreenUpdates:.

We do this in viewDidAppear so we can be sure our view has been laid out properly...

- (void)captureBlurImage {
    UIScreen *screen = [UIScreen mainScreen];
    UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, screen.scale);

    [self.view drawViewHierarchyInRect:self.view.bounds afterScreenUpdates:NO];

    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
    UIImage *blurImage = [snapshot applyDarkEffect];
    self.blurImage = blurImage;

    UIGraphicsEndImageContext();
}

Now that we have an image, we just need to add it to our panel...

    self.blurImageView = [[UIImageView alloc] initWithFrame:self.panel.bounds];
    self.blurImageView.image = self.blurImage;
    self.blurImageView.contentMode = UIViewContentModeBottom;
    self.blurImageView.clipsToBounds = YES;
    [self.panel addSubview:self.blurImageView];

Note that we're making sure that our image is rendered at full size, clamped to the bottom of the image view. We also make sure we only draw the portion of the image that is inside the blur view's frame.

In order to get the image to remain static we have to keep the image view's bottom edge aligned with the screen's bottom edge. To do this we adjust the height of the view while the panel is moving downward.

  CGRect blurFrame = self.blurImageView.frame;
  blurFrame.size.height = self.view.frame.size.height - frame.origin.y;
  self.blurImageView.frame = blurFrame;

We need to do this in the openPanel & closePanel methods, as well as the pan gesture recognizer callback.

Once we've done that we get a nice custom blur effect with none of the drawbacks of the UIToolbar approach.