728x90 AdSpace

.
Latest News

iOS 8 Visual Effects Tutorial


OS 7 raised the bar for iOS app design, especially when it came to on-screen effects.
One of the most significant changes was the use of blur throughout iOS 7, most notably in Notification Center and Control Center, as illustrated below:
blurs

However, when developers were handed the task of adding similar blur effects to their own apps, they hit a serious roadblock. Apple had decreed that the crop of devices available at the time simply weren’t powerful enough to support real-time blurs in third-party apps, claiming developers would over-use blurs resulting in slower apps with much degraded user experiences.
Developers, being the crafty individuals they are, soon created their own blurring algorithms from static blurring of images all the way to live-blur hacks.
A lot of these solutions worked pretty well. However, iOS 8 adds official, high-performance blur effects to the developer’s toolbox – and you’ll be surprised how easy it is to use the new blur effects.
Note: For one approach to using static images to mimic live blur effects, check out this blog post.

Clear Facts on Blurs

Executing blurs in a tasteful – and efficient – manner takes a bit of finesse. In this section you’ll learn about a common blurring algorithm and how you can use blurs to improve the user experience of your apps.

How Blurs Work

All blurs start with an image. To achieve a blur, you apply a blurring algorithm to each pixel in the image; the resulting image then contains an evenly blurred version of the original. Blurring algorithms vary in style and complexity, but you’ll use a common algorithm known as Gaussian blur in this tutorial’s project.
Blurring algorithms generally examine each pixel of an image and use the surrounding pixels to calculate new color values for that pixel. For example, consider the following theoretical image grid below:
blur-how1
Each cell in the above grid represents an individual pixel, and each pixel has a value between 1 and 10. Consider the case where the algorithm is evaluating the center pixel. The algorithm averages the values of the surrounding pixels and inserts this averaged value into the center pixel, resulting in the following new pixel grid:
blur-how2
You then repeat this process for every pixel in the original image.
The sample algorithm above only examined one pixel in each direction to create the new averaged value. You can expand this blur radius even further to increase the amount of blurring in your image, as demonstrated in the image below:
3px and 16px Gaussian Blur applied to an image
3px and 16px Gaussian Blur applied to an image
Note: Generally, the greater your blur radius is, the more processing power you’ll require to process the image. iOS offloads most image processing activities to the GPU to keep the main thread free.

Blur Design Strategies

Humans have a tendency to pay attention to elements that are in-focus and ignore things that aren’t. Believe it or not, this is a natural consequence of how our eyes work. Focusing on an object as it moves closer or further away from the eye is known as accommodation; it’s what helps you perceive depth and the distance of objects around you.
App designers exploit this fact and blur unimportant items on the screen to direct the user’s attention to the remaining non-blurred elements, as demonstrated in this screenshot of the popular Twitter client Tweetbot:
tweetbot
The user interface in the background is barely recognizable in the image above. This provides contextual awareness to the user about where they are in the navigational hierarchy. For instance, you’d expect to return to the blurred-out view in the background once you select one of the accounts displayed.
Note: Be careful about the overuse of blurs in your mobile apps. Although blurs can provide great-looking effects, they can be distracting and annoying if used inappropriately or too often.
Follow the standard design approach to use blurs to direct a user’s attention to things that matter and you’ll seldom go wrong. See the Designing for iOS section of the iOS Human Interface Guidelines document found on Apple’s iOS developer center for more.

Getting Started

To learn how to implement blurring, you’ll add some blur effects to a brand new Brothers Grimm fairytale app – aptly named Grimm.
The app displays a library of fairytales to the user; when the user taps on a fairytale, the app presents the full story on screen. The user can customize the display font, text alignment, or even the color theme for daytime or nighttime reading.
Start by downloading the starter project, then open Grimm.xcodeproj in Xcode. Open Grimm.storyboard and take a look at the view controllers contained in the app as illustrated below:
storyboard
You can ignore the very first view controller in the image above as it’s simply the root navigation controller of the app. Taking each numbered view controller in turn, you’ll see the following:
  1. The first controller is a StoryListController, which displays a list of all of the fairy tales in the database.
  2. When you tap a story in the list you segue to this detail StoryViewController, which displays the title and text of the selected fairy tale.
  3. This is a OptionsController which is contained inside the StoryViewController and displays the available font, text alignment, and color options. To display it simply tap the settings icon in the detail controller.
Build and run. You’ll see the initial screen as shown below:
firstrun
Have some fun exploring the app; select different stories, tap the ellipsis icon and swipe to different fonts and reading modes to understand how the user interface functions.
Note: You can use any simulator or iOS 8 device to run this app except for the iPad 2. Apple disabled much of the iPad 2’s ability to display blurs for performance reasons. The app will still work on the iPad 2; however, you won’t be able to see any of the neat blur effects.
Once you have a handle on how the app behaves, head straight to the next section to learn how to apply blur effects to the app.

Manual Blur Techniques

Eagle-eyed readers might have noticed that there is some legacy Objective-C code in this project.
objcnswift
Don’t fret – the category in this project has been used by hundreds of apps and is incredibly solid. It’s been bridged into all of your Swift files using Grimm-Bridging-Header.h since there’s no need to rewrite it for Swift.
Note: Swift was architected to be interoperable with Objective-C so that developers, including Apple’s own developers, could start adding Swift code to their projects without refactoring. Bridging Headers allow you to include Objective-C with your Swift files.
To have a look at the category in question, open Grimm\Categories\UIImage+ImageEffects.m from theProject Explorer and skim past all of the legalese comments until you findapplyBlurWithRadius:tintColor:saturationDeltaFactor:maskImage:. This tutorial won’t cover all of the ins and outs of this code, but it will help you to understand some of the basic functionality contained within.
Apple provided this UIImage category with the release of iOS 7 to demonstrate how to apply static blurs to images. It takes advantage of the Accelerate.framework to simplify vector and matrix math and is optimized for image processing.
applyBlurWithRadius:tintColor:saturationDeltaFactor:maskImage: takes a blur radius, saturation delta and an optional masking image and uses a lot of math to produce a new, manipulated image.

Obtaining a Snapshot Image

You’ll need a snapshot image before you can apply your blur effect. The focus of your efforts today (pardon the pun) will be the options drawer at the bottom of the StoryViewController view.
Open StoryViewController.swift and look for setOptionsHidden:. You’ll capture a screenshot of the entireStoryViewController view, blur it, and then set it as a subview of the options view.
Add the following function above setOptionsHidden::
func updateBlur() {
  // 1
  optionsContainerView.hidden = true  
  // 2
  UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, true, 1)
  // 3
  self.view.drawViewHierarchyInRect(self.view.bounds, afterScreenUpdates: true)
  // 4
  let screenshot = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
}
Taking each numbered comment in turn:
  1. You don’t want a screenshot of the options if they are on the screen so make sure that they are hidden before you take the screenshot.
  2. In order to draw a screenshot you need to create a new image context. Since you’re going to blur the image anyway, you don’t need the full-resolution Retina image. This saves a ton of processing power and resources.
  3. Draw the story view into the image context. Since you have hidden the optionsContainerView, you will need to wait for screen updates before capturing a screenshot.
  4. Pull a UIImage from the image context and then clean it up.
You will need to actually call updateBlur when you tap on the ellipsis icon. Find the method setOptionsHidden:and add the following code to the top of the function:
if !hidden {
  updateBlur()
}
Before you go any further, you should check that you’re capturing the screenshot you think you’re capturing.
Add a breakpoint to the UIGraphicsEndImageContext() line you added to updateBlur in the previous step. Then build and run your project and tap on any fairy tale to select it.
Once that fairy tale is displayed, tap the ellipsis icon to trigger the breakpoint. In the variables section of theDebugging Editor, expand the screenshot variable and select the nested Some variable, as shown below:
debug1
Tap the Space key to open Quick Look; you should see a non-Retina screenshot of your story view, as you can see here:
debug2
Notice there aren’t any navigation elements from your UINavigationController; that’s because the story view is a subview of UINavigationController, therefore the navigation controls are outside of the screenshot area.
Now that you’re sure you are grabbing the correct screenshot, you can continue on and apply the blur using theUIImage category described earlier.

Blurring Your Snapshot

Still working in StoryViewController.swift, find the updateBlur function you added and add the following line directly below UIGraphicsEndImageContext():
let blur = screenshot.applyLightEffect()
Next, move your breakpoint to the line you just added, like so:
debug3
Note: You can move a breakpoint by dragging it up and down in the gutter.
Build and run. Tap any fairy tale, then tap the ellipsis icon in the navigation bar. Over in the Debugging Editor, find the blur variable and hit the space key to open Quick Look again.
Wait a second… there’s nothing there! What gives?
You don’t see anything because your breakpoint is right on the line that sets the variable and Xcode stopped executing just before it executed that line.
To execute the highlighted line of code, press F6 or click the Step over button as shown below:
debug4
Now expand the blur variable, select the nested Some variable, then press the Space key to open up Quick Look and see your new blurred image:
debug5
Note: LLDB (the debugging tool that Xcode uses) sometimes doesn’t play nicely with Swift. You may have to click the Step over button twice to get the Some variable to appear.
You’ve managed to take a screenshot and blur it – now it’s time to add the blurred image to the app.

Displaying the Blur in Your View

Open StoryViewController.swift and add the following property to the top of the file along with the other the property declarations:
var blurView = UIImageView()
This initializes a new UIImageView with every StoryViewController instance.
Find viewDidLoad and add the following code to the very end of the method:
optionsContainerView.subviews[0].insertSubview(blurView, 
               atIndex:0)
Grimm.storyboard puts OptionsController inside a container view displayed each time the user taps the ellipsis icon. Since you don’t have direct access to the OptionsController view, you need to get the first subview of the container. In this case, that happens to be the view owned by OptionsController. Finally, you insertblurView as a subview to the very bottom of the view stack, beneath all other subviews.
Still inside StoryViewController.swift, go back to updateBlur and add the following code to the end of the function:
blurView.frame = optionsContainerView.bounds
blurView.image = blur
 
optionsContainerView.hidden = false
Since blurView isn’t setup in the Storyboard, it will have a frame of CGRectZero unless you set the frame manually. As well, you set the image property to the blurred image you’ve already created.
Also remember that you hid optionsContainerView before taking the screenshot. You need to make sure that you unhide the view before the method ends.
Disable the breakpoint you set earlier, then build and run. After selecting a fairy tale and displaying the options, behold the blur in all its glory, as shown below:
manualblur1
Hmm. That blur looks kind of… funky, doesn’t it? Why doesn’t it match up with the text behind it?
By default, UIImageView resizes the image to match the frame of the view. That means the huge blurred image is being squished into much smaller options view. That just won’t do!
To fix this, you need to set the UIImageView’s contentMode property to something other than the defaultUIViewContentMode.ScaleToFill.
Add the following line to updateBlur, just below where you set the blurred image on blurView:
blurView.contentMode = .Bottom
The UIViewContentMode.Bottom content mode forces the image to retain its size and instead fixes it to the bottom-center of the UIImageView.
Build and run. How does the blur-positioning look now?
manualblur2
There’s just one more thing you need to do before your static blur is ready for use. Try changing the orientation of your device or simulator (⌘-Left/Right Arrow). The view doesn’t resize!
Since you are using auto-layout with all of your text, the screenshot wont be valid anymore. You’ll need to take a new screenshot after the device rotates and update the blurView.
To do this, add the following function override inside StoryViewController.swift:
override func viewWillTransitionToSize(size: CGSize,
  withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
  // 1
  coordinator.animateAlongsideTransition(nil, completion: { context in
    // 2
    self.updateBlur()
  })
}
  1. The animateAlongsideTransition method gives you the ability to animate changes alongside the orientation change as well as do some cleanup after the rotation completes. You’re only using the completion block since you need the frame of the optionsViewController after the rotation change.
  2. Setup the blur again after the orientation animation completes. This will use the new text layout.
Build and run and try changing the orientation to see everything be resized with a new blur.
manualblur3
The blur is sized correctly, but it’s not terribly exciting. Scroll the text behind the blur and you’ll notice the blur doesn’t update.
You can definitely do better than that. iOS 8 gives you the tools to create dynamic, real-time blur effects in your apps – it’s a long way from the developer-led solutions of iOS 7!

Blur Effects in iOS 8

iOS 8 gives you an entire suite of visual effects goodies; UIBlurEffect, a subclass of UIVisualEffect, is particularly relevant to your interests. UIBlurEffect provides the nice blurs you see in navigation bars, Notification Center, and Control Center – and you can use it in your apps as well.

Adding a UIBlurEffect

Open StoryViewController.swift and find setOptionsHidden:. Comment out the call to updateBlur inside the if-statement you built up in the previous sections; afterwards it should look like the following screenshot:
newblur1
While you’re at it, you should ensure blurView isn’t added to the scene at all. In viewDidLoad, comment out the following line:
optionsContainerView.subviews[0].insertSubview(blurView, 
               atIndex:0)
Note: Instead of deleting all the code you added previously, you’re simply commenting it out so you can go back and see the differences. If you’re not interested in preserving your manual blur code, you can delete the code instead of commenting it out.
Build and run. Check that everything compiles and that your static blur is gone.
Open Grimm.storyboard and find the Options Controller Scene. Select the view, open the Attributes Inspector and change the view’s Background to Clear Color, as shown below:
newblur2
Open OptionsController.swift and add the following code to viewDidLoad, just after the line where you addoptionsView to the view:
// 1
let blurEffect = UIBlurEffect(style: .Light)
// 2
let blurView = UIVisualEffectView(effect: blurEffect)
// 3
blurView.setTranslatesAutoresizingMaskIntoConstraints(false)
view.insertSubview(blurView, atIndex: 0)
Taking each numbered comment in turn:
  1. Create a UIBlurEffect with a UIBlurEffectStyle.Light style. This defines which effect to use. The other available styles are UIBlurEffectStyle.ExtraLight and UIBlurEffectStyle.Dark.
  2. Create a UIVisualEffectView and tell it which effect to use. This class is a subclass of UIView; its sole purpose is to define and display complex visual effects.
  3. Disable translating the auto-resizing masks into constraints on the blurView, as you’ll manually add constraints in just a moment, and add it at the bottom of view stack. If you just added blurView on top of the view, it would end up blurring all of the controls underneath it instead!
Now you need to ensure your blurView lays out properly with the rest of the view.
Still working in viewDidLoad, add the following code just before the line where you call addConstraints::
constraints.append(NSLayoutConstraint(item: blurView,
  attribute: .Height, relatedBy: .Equal, toItem: view,
  attribute: .Height, multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: blurView,
  attribute: .Width, relatedBy: .Equal, toItem: view,
  attribute: .Width, multiplier: 1, constant: 0))
These constraints keep the frame of the blurView consistent with that of the OptionsController view.
Build and run. Select a fairy tale, and then scroll the text. Behold as the blur updates in real-time.
newblur3
You now have a dynamic blur effect in your app that not only looks great – you’re also using core iOS functionality to make it happen.

Adding Vibrancy to your Blur

Blur effects are great – but as usual, Apple has taken it to the next level with the UIVibrancyEffect, which when used in combination with UIVisualEffectView adjusts the colors of the content to make it feel more vivid.
The following image demonstrates how vibrancy makes your labels and icons pop off the screen, while at the same time blending with the background itself:
vibrancy
The left side of the image shows a normal label and button, while the right side shows a label and button with vibrancy applied.
Note: UIVibrancyEffect must be added to a UIVisualEffectView that has been setup and configured with aUIBlurEffect object; otherwise, there won’t be any blurs to apply a vibrancy effect!
Open OptionsController.swift and add the following code to viewDidLoad, just before where the Auto Layout constraints are added:
// 1
let vibrancyEffect = UIVibrancyEffect(forBlurEffect: blurEffect)
// 2
let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
vibrancyView.setTranslatesAutoresizingMaskIntoConstraints(false)
// 3
vibrancyView.contentView.addSubview(optionsView)
// 4
blurView.contentView.addSubview(vibrancyView)
Taking each numbered comment in turn:
  1. Create a UIVibrancyEffect that uses the blurEffect you set up earlier. UIVibrancyEffect is another subclass of UIVisualEffect.
  2. Create a UIVisualEffectView to contain the vibrancy effect. This process is exactly the same as creating a blur. Since you’re using Auto Layout, you make sure to disable auto-resizing translations here.
  3. Add the optionsView to your vibrancy view’s contentView property; this ensures the vibrancy effect will be applied to the view that contains all of the controls.
  4. Finally you add the vibrancy view to the blur view’s contentView to complete the effect.
The final thing to do is set up the Auto Layout constraints for the vibrancy view so that it uses the same height and width of your controller’s view.
Add the following constraints below the others, at the end of viewDidLoad:
constraints.append(NSLayoutConstraint(item: vibrancyView,
  attribute: .Height, relatedBy: .Equal,
  toItem: view, attribute: .Height,
  multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: vibrancyView,
  attribute: .Width, relatedBy: .Equal,
  toItem: view, attribute: .Width,
  multiplier: 1, constant: 0))
Build and run. Bring up the options view to see your new vibrancy effect in action:
effect1
Unless you have high-contrast vision, the vibrancy effect makes it really difficult to read the labels and controls. What’s going on?
Ah – the content behind the blur view is white and you’re applying a UIBlurEffectStyle.Light effect. That’s counterproductive, to be sure.
Modify the line near the top of viewDidLoad that initializes the blurEffect:
let blurEffect = UIBlurEffect(style: .Dark)
This changes the blur effect to add more contrast between the background and text.
Build and run. You’re now experiencing some true vibrancy:
effect2

Where To Go From Here?

You can download the finished project here.
You’ve seen how to blur images manually, as well as how to create real-time blur effects. You can just as easily add UIVisualEffectViews to your own apps.
Your manual blur technique relied on static images, so you couldn’t animate the image or efficiently update the blur in real-time. UIBlurEffect, however, does update in real-time, so you can achieve all sorts of weird and wonderful things with these effects, such as animations etc.
Whilst you might be tempted to go ahead and blur all the things, keep in mind what was talked about earlier in the tutorial regarding using these effects sparingly and only where appropriate. As is often the case, with blur and vibrancy, less is definitely more.
If you want to learn more about new iOS 8 APIs like this, check out our book iOS 8 by Tutorials, where we have 30 chapters and over 750 pages of tutorials on new iOS APIs ready for you!
In the meantime, if you have any questions or comments about this tutorial or visual effects in general, please join the forum discussion below!
  • Blogger Comments
  • Facebook Comments

0 comments:

Post a Comment

Item Reviewed: iOS 8 Visual Effects Tutorial Rating: 5 Reviewed By: Unknown