Chapter 21: Storyboards and Custom Transitions

Prior to iOS 5, interface elements and views were created using Interface Builder (IB) and saved in nib files. Storyboards are a new way to create interfaces, and in addition to creating interface elements, you can now specify the navigation (called segues) between those interfaces. This was something you could not do previously without writing code. You can think of storyboards as a graph of all your view controllers connected by segues that dictate the transition between them.

The benefits of storyboards don’t stop there. They also make it easy for developers to create static table views without a data source. How many times have you wanted to create a table view that’s not bound to a real data source—for example, a table that shows a list of options instead of data. A common use for this is your app’s settings page. Storyboards also help co-developers and clients understand the complete workflow of the app.

Storyboards aren’t all romantic, and in my opinion, there are some significant drawbacks. Later in this chapter, we show you how to use storyboards without being hurt by those drawbacks.

We begin with how to start using storyboards and how to do things using storyboards that you do with nib files, such as communicating between controllers. In the “Static Tables” section later in this chapter, you find out how to create a static table view without a data source. Finally, you discover the most interesting aspect of storyboards—writing your own custom transition animations. Although these cool transition animations sound complicated, Apple has made it really easy to write them.

Getting Started with Storyboards

You can use storyboards for new projects or add them to an existing project that doesn’t have a storyboard yet. For existing projects, you can add storyboards in the same way you add a new file to a project. You find more on how to instantiate view controllers in this storyboard in the upcoming section “Instantiating a Storyboard.”

For new projects, storyboards can be created in Xcode 4.5 by using the new project template and selecting to use the default Use Storyboard option, as shown in Figure 21-1.

When you create a new project using storyboards, the info.plist key of your app contains a key called UIMainStoryboardFile. This key supersedes NSMainNibFile that was used prior to iOS 5. You can continue to use NSMainNibFile if your app’s main window is loaded from a nib file instead of a storyboard. However, you can’t use both UIMainStoryboardFile and NSMainNibFile in the same app. UIMainStoryboardFile takes precedence, and your nib file specified in NSMainNibFile never gets loaded.

9781118449974-fg2101.tif

Figure 21-1 New Project Template in Xcode 4.5 showing the Use Storyboard option

Your application can store the complete storyboard in one file, and IB automatically builds it into separate files optimized for loading. In short, you don’t have to be worried about loading time or performance when using storyboards.

Instantiating a Storyboard

When your UIMainStoryboardFile is set, the compiler automatically generates code for instantiating it and loads it as your application’s startup window. If you’re adding storyboards in an existing app, you do so programmatically. The methods for instantiating view controllers within a storyboard are defined in the UIStoryboard class.

When you want to display a view controller specified in your storyboard, you load the storyboard using this method:

+ storyboardWithName:bundle:

Loading View Controllers Within a Storyboard

Loading view controllers within a storyboard is very similar to the nib loading method, and with the UIStoryboard object, you can instantiate view controllers using the following method:

– instantiateInitialViewController

– instantiateViewControllerWithIdentifier:

Segues

Segues are transitions defined in your storyboard file. UIKit provides two default transition styles, Push and Modal. They behave similar to the pushViewController:animated:completion: and presentViewController:animated:completion methods you use in iOS 5. In addition to this, you can create custom segues and create new kinds of transitions between view controllers. You look at this later in this chapter in the section “Custom Transitions.”

You create segues by connecting certain events on view controllers with other view controllers on your storyboard file. You can drag from a button to a view controller, from a gesture recognizer object to a view controller, and so on. IB creates a segue between them, and you can select the segue and use the attributes inspector panel to modify the transition styles.

The attributes inspector panel also allows you to set a custom class if you select a custom transition style. You can think of a segue as something that connects an action with a transition. Actions that can trigger segues can be button tap events, row selection events on static table views, a recognized gesture, or even audio events. The compiler automatically generates the necessary code to perform a segue when the event to which you connected the segue occurs.

When a segue is about to be performed, a prepareForSegue:sender: method is invoked on the source view controller and an object of type UIStoryboardSegue is passed to it. You can override this method to pass data to the destination view controller. The next section explains how to do this.

When a view controller performs multiple segues, the same prepareForSegue:sender: method gets called for every segue. To identify the performed segue, use the segue identifier to check whether the performed segue is the intended one and pass data accordingly. As a defensive programming practice, I advise you to perform this check even if the view controller performs only one segue. This ensures that later on when you add a new segue, your app will continue to run without crashing.

Passing Data

With iOS 5, when you use storyboards, instantiating view controllers and presenting them to the user are done automatically. You’re given a chance to fill in data by overriding the prepareForSegue:sender: method. By overriding this method, you can get the pointer to the destination view controller and set the initial values there.

The framework calls the same methods that you used before, such as viewDidLoad, initWithCoder: and NSObject’s awakeFromNib method, which means that you can continue writing your view controller’s initialization code as with iOS 4.

Returning Data

With storyboards, you communicate data back to the parent view controller exactly as you do with nib files or manually coded user interfaces. Data created/entered by the user on modal forms that you present can be retuned to the parent via delegates or blocks. The only difference is that on your parent view controller, you have to set the delegate in the prepareForSeque:sender: method to self.

Instantiating Other View Controllers

UIViewController has a storyboard property that retains a pointer to the storyboard object (UIStoryBoard) from which it was instantiated. This property is nil if your view controller is created manually or from a nib file. With this back reference, you can instantiate other view controllers defined in your storyboard from any other view controller. You do so by using the view controller’s identifier. The following method on UIStoryBoard allows you to instantiate a view controller using its identifier:

– instantiateViewControllerWithIdentifier:

As a result, you can still have view controllers on your storyboard that aren’t connected with any other view controllers through segues, and yet these view controllers can be initialized and used.

Performing Segues Manually

While storyboards can automatically trigger segues based on actions, in some cases, you may need to perform segues programmatically. You might do so to deal with actions that cannot be handled by the storyboard file. To perform a segue, you call the performSegueWithIdentifier:sender: method of the view controller. When you perform segues manually, you can pass the caller and the context objects in the sender parameter. This sender parameter will be sent to the prepareForSegue:sender: method later.

Unwinding Segues

Storyboards originally allowed you to instantiate and navigate to view controllers. iOS 6 introduces methods in UIViewController that allows unwinding segues. By unwinding segues, you can implement methods to navigate “back” without creating additional view controllers.

You can add unwinding support to a segue by implementing an IBAction method in your view controller that takes a UIStoryboardSegue as a parameter. Example

-(IBAction)unwindMethod:(UIStoryboardSegue*)sender {

}

You can now connect an event in a view controller to its Exit object. Xcode automatically enumerates all possible unwind events (any method that is an IBAction and accepts a UIStoryboardSegue as a parameter) in a storyboard and allows you to connect to them. This is shown in Figure 21.2.

Building Table Views with Storyboard

One important advantage of storyboards is the capability to create static tables from IB. With storyboards, you can build two types of table views: a static table that doesn’t need a special class for providing a data source, and a table view containing a prototype cell (similar to custom table view cells in iOS 4) that binds data from a model.

9781118449974-fg2102.tif

Figure 21.2 Connecting a IBAction for unwinding a segue

Static Tables

You can create static tables in your storyboard by dragging a table, selecting it, and from the attributes inspector, choosing Static Cells, as shown in Figure 21-3.

9781118449974-fg2103.tif

Figure 21-3 A storyboard illustrating static table view creation

Static cells are great for creating settings pages (or pages whose content doesn’t come from a Core Data model or a web service or any such data source) as in Apple’s Settings app.

Static cells can be created only for table views that are from a UITableViewController. You cannot create static cells for table views that are added as a subview of a UIViewController view.

Prototype Cells

Prototype cells are similar to custom table view cells, but instead of creating prototype cells on separate nib files and loading them in the data source method cellForRowAtIndexPath:, you create them in IB on your storyboard and just set the data on your data source methods.

You identify all prototype cells using a custom identifier, which ensures proper functioning of the table view cell queuing methods. Xcode will warn you if a prototype cell in your storyboard doesn’t have a cell identifier.

Custom Transitions

Another advantage of storyboards is that you can now easily create custom transition effects for your view controllers.

When segues are performed, the compiler generates necessary code to present or push the destination controller based on the transition style you set on your storyboard. You’ve found that two types of transition styles, Push and Modal, are supported natively by iOS. There’s also a third type, Custom. Choose Custom and provide your own subclass of UIStoryboardSegue that handles your custom transition effects.

Create a subclass of UIStoryBoardSegue and override the perform method. In the perform method, access the pointer to the source view controller’s main view’s layer and do your custom transition animation (using Core Animation). Once the animation is complete, push or present your destination view controller (you can get a pointer to this from the segue object). It’s as simple as that.

To illustrate, we’ll show you how to create a transition where the master view gets pushed down, and the details view appears at the bottom of the screen.

Create a new application using the Master-Details template and open the MainStoryboard. Click on the only segue and change the type to Custom. Add a UIStoryboardSegue subclass and override the perform method and paste the following code.

Custom Transition Using a Storyboard Segue

- (void) perform {

  

  UIViewController *src = (UIViewController *)self.sourceViewController;

  UIViewController *dest = (UIViewController *)self.destinationViewController;

  

  CGRect f = src.view.frame;

  CGRect originalSourceRect = src.view.frame;

  f.origin.y = f.size.height;

  [UIView animateWithDuration:0.3 animations:^{

    src.view.frame = f;

    

  } completion:^(BOOL finished){

    src.view.alpha = 0;

    dest.view.frame = f;

    dest.view.alpha = 0.0f;

    [[src.view superview] addSubview:dest.view];

    [UIView animateWithDuration:0.3 animations:^{

      

      dest.view.frame = originalSourceRect;

      dest.view.alpha = 1.0f;

    } completion:^(BOOL finished) {

      

      [dest.view removeFromSuperview];

      src.view.alpha = 1.0f;

      [src.navigationController pushViewController:dest animated:NO];

    }];    

  }];

}

That’s it. You can do all kinds of crazy stuff with the layer pointers of the source and destination view controllers. Justin Mecham has open-sourced a great example of a doorway transition (ported from Ken Matsui) on GitHub. (https://github.com/jsmecham/DoorwaySegue). You can also create your own transition effects by manipulating the layer pointers of the source and destination view controllers. With segues, creating a custom transition effect is far easier.

Another Advantage

When you use storyboards, it becomes easy for co-developers (and clients) to understand the app’s flow. Instead of going through multiple nib files and cross-referencing the instantiation code for understanding the flow, co-developers can open the storyboard file and see the complete flow. This alone is a compelling reason to use them.

Disadvantages

Last year, when storyboards were introduced, they had a drawback. They could be used only on apps that are targeting iOS 5+ customers. With the release of iOS 6, you don’t need to worry about this issue.

Avoiding the Merge Conflict Hell

Storyboards have another, rather annoying, problem for teams. The default application template in Xcode has one storyboard for the entire app. This means that when two developers work on the UI at the same time, merge conflicts become inevitable, and because storyboards are internally auto-generated XML, these merge conflicts are too complicated to fix. I recommend that you break your storyboards into multiple files, one file for every use case. In most cases, one developer will be working on a use case, and the chances of ending up with a merge conflict are lower.

Examples of use case-based storyboards for a Twitter client are Login.Storyboard, Tweets.Storyboard, Settings.Storyboard, and Profile.Storyboard. Instead of breaking the storyboard up into multiple nib files (going back to square one), use multiple storyboards to help solve the merge conflict issue while preserving the elegance of using storyboards. As I mention in the previous paragraph, a developer who is working on the Login use case will probably not be working on Tweets at the same time.

Customizing Your Views Using UIAppearance Protocol

This last section covers an important addition to iOS 5: a method to customize your view appearance through Apple’s native classes. Prior to iOS 5, customizing the look and feel of native controls was not natively supported and was often difficult for developers. A common problem that developers face is changing the appearance of all instances of a control. The proper way of doing so was to create the complete control from scratch. But because that was time-consuming, some developers resorted to overriding or swizzling methods like drawRect:.

Beginning with iOS 5, Apple provided default support for most UIKit controls by formalizing customization using a couple of protocols—namely, UIAppearance and UIAppearanceContainer. Any UI control that adheres to the UIAppearance protocol can be customized to have a different look and feel. Want more? The UIAppearance protocol even allows you to specify a different look and feel based on where the control is contained. That is, you can specify that the appearance of a control (say the tintColor of a UIBarButtonItem) be different when it’s contained within a specific view (UINavigationBar or UIPopoverViewController). You do so by getting an appearance proxy object for the control’s class and using that to customize the appearance. Here’s an example.

To customize the tint color of a bar button throughout your application, set the tintColor to the UIBarButtonItem’s appearance proxy like this:

[[UIBarButtonItem appearance] setTintColor:[UIColor redColor]];

Note that the setTintColor method existed in iOS 4, in UIBarButtonItem. But it was applicable only to a particular instance of the control. With the appearance proxy object, you’re able to customize the appearance of any object created using the said class.

On similar lines, you can also customize the appearance of a control depending on the contained view by using the following method:

[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:[UIColor redColor]];

The first parameter is a nil terminated list of all container classes like UINavigatorBar, UIPopOverController that conforms to the UIAppearanceContainer protocol.

Starting with iOS 5, most UI elements added support to the UIAppearance protocol. Additionally, controls like the UISwitch in iOS 5 allow you to easily change the color of the “on” gradient to the designer’s choice. Now, how do you know when all elements (and which properties of those elements) can be customized through UIKit’s appearance proxy? There are two ways. The old way is to open the documentation and read it. The other way is a shortcut that most developers use: reading the header file. When you open the header file for the corresponding UIKit element, any property that is tagged with UI_APPEARANCE_SELECTOR supports customization through its appearance proxy. For example, the UINavigationBar.h has the property tintColor tagged with UI_APPEARANCE_SELECTOR:

@property(nonatomic,retain) UIColor   *tintColor  UI_APPEARANCE_SELECTOR;

This means, you can call

[[UINavigationBar appearance] setTintColor:newColor];

Although Apple was against UI customization in the beginning (on both Mac and iOS), that is slowly changing, and you can see that Apple’s native apps (such as the new Reminders app) have heavily customized skeuomorphic user interfaces. With the UIAppearance protocol, you can achieve the same result with far less code.

Summary

In this chapter you learnt about storyboards and methods to implement custom transitions using Storyboards. You also learnt about unwinding segues that are introduced in iOS 6 and the advantages and dis advantages of using storyboards in your app. Lastly, you learn about UI customization using the appearance proxy protocol.

Further Reading

Apple Documentation

The following documents are available in the iOS Developer Library at developer.apple.com or through the Xcode Documentation and API Reference.

What’s New in iOS 6

TableView Programming Guide

TableViewSuite

UIViewController Programming Guide

WWDC Sessions

The following session video is available at developer.apple.com.

WWDC 2011, “Session 309: Introducing Interface Builder Storyboarding”

WWDC 2012, “Session 407: Adopting Storybaords in Your App”

Other Resources

enormego / EGOTableViewPullRefreshhttps://github.com/enormego/EGOTableViewPullRefresh

jsmecham / DoorwaySeguehttps://github.com/jsmecham/DoorwaySegue

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.74.205