Implementing Multiple Scenes and Popovers

What You’ll Learn in This Hour:

Image How to create multiple scenes in the storyboard

Image The use of segues to transition between scenes

Image Ways to transfer data between scenes

Image How to present and use popovers


This hour marks a major milestone in your iOS app development capabilities. In the preceding hour’s lesson, you learned about alert views and action sheets. These were the first user interface (UI) elements we’ve explored that act as (somewhat) independent views that a user interacts with. You’ve also seen how to hide and show views, making it possible to customize your user interface. All of these, however, took place within a single scene. That means that no matter how much was going on onscreen, we used a single view controller and a single initial view to deal with it. In this hour, we break through those limits and introduce the ability to create applications with multiple scenes—in other words, multiple view controllers and multiple views.

In this lesson, you learn how to create new scenes and the new view controller classes you need to back them up. You also learn how to visually define your transitions between scenes and trigger them automatically, or programmatically. In addition, you will explore the use of popovers to present information within a pseudo “window” on the display.

Before we begin, I want to add a disclaimer: In this hour, you learn several different ways to accomplish the same thing. Apple changes the iOS often, and despite their somewhat elegant software development kit (SDK), you will encounter inconsistencies. The takeaway is that you should do what you feel comfortable with. There are plenty of “clever” solutions to problems that result in code that, although correct, is never going to make sense to anyone but the person who wrote it.


Introducing Multiscene Storyboards

We’ve been able to build apps that do quite a few things using a single view, but many don’t lend themselves to a single-view approach. It’s rare to download an app that doesn’t have configuration screens, help screens, or other displays of information that go beyond the initial view that is loaded at runtime.

To use features like these in your apps, you need to create multiple scenes in your storyboard file. Recall that a scene is defined by the presence of a view controller and a view. You’ve been building entire applications in one view with one view controller for the past six hours. Imagine how much functionality you could introduce with unlimited scenes (views and view controllers). With the iOS project storyboard, that’s exactly what you can do.

Not only that, but you can literally “draw” the connections between different scenes. Want to display an information screen if the user touches a Help button? Just drag from your button to a new scene. It “just works.” Figure 11.1 shows a multiscene application design with segues.

Image

FIGURE 11.1 A multiscene application design.

The Terminology

Before we head into multiscene development, we should introduce/review a few pieces of terminology, several of which you’ve learned previously but may not have really had to think about until now:

Image View controller: A class that manages the user’s interactions with his iDevice. In many of the tutorials in this book, single-view controllers are used for most of the application logic, but other types exist (and are used in the coming hours).

Image View: The visual layout that a user sees onscreen. You’ve been building views in view controllers for quite awhile now.

Image Scene: A unique combination of view controller and view. Imagine you’re building an image-editing application. You may choose to develop scenes for selecting files to edit, another scene for implementing the editor, another for applying filters, and so on.

Image Segue: A segue is a transition between scenes, often with a visual transition effect applied.There are multiple types of segues available depending on the type of view controller you’re using.

Image Exit: The Exit icon appears in each scene in your storyboard. The exit can be used to transition back to a previous scene. If you display four scenes in sequence, for example, and want to move from the fourth back to the first, you would use the first scene’s exit.

Image Unwind: The process of moving back to an earlier sceneby way of the exit. This is considered an “unwind” segue.

Image Modal views: A modal view is one that is displayed over top of an original view when user interactions are required. You will mostly be using modal views (by way of the modal segue type) in this book.

Image Relationship: A “segue” of sorts for certain types of view controllers, such as the tab bar controller.Relationships are created between buttons on a master tab bar that display independent scenes when touched. You learn about these in Hour 13, “Advanced Storyboards Using Navigation and Tab Bar Controllers.”

Image Storyboard: The file that contains the scene, segue, and relationship definitions for your project.

You must create new class files to support the requirement for multiple view controllers; so, if you need a quick refresher on adding new files to Xcode, refer to Hour 2, “Introduction to Xcode and the iOS Simulator.” Other than that, the only prerequisite is the ability to Control-drag, something you should be very good at by now.


A Different Perspective

I’ve just described what the different pieces are that you need to know to create a multiscene application, but this doesn’t necessarily help you conceptualize what Apple’s “storyboarding” concept is going for.

Think of it this way: A storyboard provides an area where you can sketch out, visually, your application’s visual design and workflow. Each scene is a different screen that your user will encounter. Each segue is a transition between scenes. If you’re the type of person who thinks visually, you’ll find that, with a little practice, you can go from a paper sketch of an application’s operation and design to a working prototype in the Xcode storyboard very, very quickly.


Preparing a Multiscene Project

To create an application with multiple scenes and segues, you must first know how to add new view controller and view pairings to your project. For each of these, you also need supporting class files where you can code up the logic for your additional scenes. To give you a better idea of how this works, let’s use a typical Single View Application template as a starting point.

As you’re well aware, the Single View Application template has a single view controller and a single view (in other words, a single scene). This doesn’t mean, however, that we’re stuck with that configuration. You can expand a single view application to support as many scenes as you want; it just provides us with a convenient starting point.

Adding Additional Scenes to a Storyboard

To add a new scene to a storyboard, open the storyboard file (Main.storyboard) in the Interface Builder (IB) editor. Next, make sure that the Object Library (Control-Option-Command-3) is open and type view controller in the Search field to show the view controller objects that are available, as shown in Figure 11.2.

Image

FIGURE 11.2 Find the view controller objects in the Object Library.

Next, drag the view controller into an empty portion of IB editor area. The view controller will add itself, with a corresponding view, to your storyboard, and just like that, you’ll have a new scene, as shown in Figure 11.3. You can drag the new view around in the storyboard editor to position it somewhere convenient. By default, the new view controller’s simulated size will not be set. Once you connect it to another view via a segue, however, it automatically inherits that view’s simulated size.

Image

FIGURE 11.3 Adding a new view controller/view creates a new scene.


Note

If you find it difficult to grab and drag the new view around in the editor, use the object dock above it. It provides a convenient handle for moving the object around.


Naming Scenes

After adding a new scene, you’ll notice there’s a bit of a problem brewing in the document outline area (Editor, Show Document Outline). By default, each scene is named based on its view controller class. We’ve been using a view controller class called ViewController, so the document outline shows the default scene as View Controller Scene. Once we add a new scene, it doesn’t have a view controller class assigned yet, so it also appears as View Controller Scene. Add another, and that scene also appears as View Controller Scene (and so on).

To deal with the ambiguity, you have two options: First, you can add and assign view controller classes to the new scenes. We’re going to do this anyway, but sometimes it’s nicer to have a plain English name for a scene that can be anything we want without it reflecting the underlying code. (“John’s Awesome Image Editor Scene” makes a horrible name for a view controller class.) The second option is to label a scene using any arbitrary string you want. To do this, select its view controller in the document outline, and then open the Identity Inspector and expand the Document section, as shown in Figure 11.4. Use the Label field to enter a name for the scene. Xcode automatically tacks Scene onto the end, so there’s no need to add that.

Image

FIGURE 11.4 Label the view controller to help differentiate between scenes.

An even faster approach is to select the View Controller line in the Document Outline and press Return; the item is then immediately editable directly in the outline. You can apply this practice to the view controller to set the scene name, or to any objects within the scene to set easy-to-understand labels.

Adding Supporting View Controller Subclasses

After establishing the new scenes in your storyboard, you need to couple them to actual code. In the Single View Application template, the initial view’s view controller is already configured to be an instance of the ViewController class—implemented by editing the ViewController.swift file. We need to create similar files to support any new scenes we add.


Note

If you’re just adding a scene that displays static content (such as a Help or About page), you don’t need to add a custom subclass. You can use the default class assigned to the scene, UIViewController, but you won’t be able to add any interactivity.


To add a new subclass of UIViewController to your project, make sure that the project navigator is visible (Command-1), and then click the + icon at the bottom-left corner of the window. When prompted, choose iOS, the Source category, then Cocoa Touch Class, and click Next. Now, select a subclass of UIViewController, as shown in Figure 11.5. You’ll also be asked to name your class. Name it something that differentiates it from other view controllers in your project. EditorViewController is better than ViewControllerTwo, for example. Make sure that Language is set to Swift, and then click Next.

Image

FIGURE 11.5 Choose the UIViewController subclass.

Finally, you’re prompted for where to save your new class. Use the group pop-up menu at the bottom of the dialog to choose your main project code group, and then click Create. Your new class is added to the project and ready for coding, but it still isn’t connected to the scene you defined.

To associate a scene’s view controller with the UIViewController subclass, shift your attention back to the IB editor. Within the document outline, select the view controller line for the new scene, and then open the Identity Inspector (Option-Command-3). In the Custom Class section, use the drop-down menu to select the name of the class you just created (such as EditorViewController), as shown in Figure 11.6.

Image

FIGURE 11.6 Associate the view controller with the new class.

After the view controller is assigned to a class, you can develop in the new scene exactly like you developed in the initial scene, but the code will go in your new view controller’s class. This takes us most of the way to creating a multiscene application, but the two scenes are still completely independent. If you develop for the new scene, it’s essentially like developing a new application; there is no way for the scenes to work together and no way to transition between them.

Creating a Segue

Creating a segue between scenes uses the same Control-drag mechanism that you have (hopefully) become very fond of over the first half of this book. For example, consider a two-scene storyboard where you want to add a button to the initial scene that, when clicked, will transition to the second scene. To create this segue, you Control-drag from the button to the second scene’s view controller (targeting either the visual representation of the scene itself, or the view controller line in the document outline), as shown in Figure 11.7.

Image

FIGURE 11.7 Control-drag from the object to the new scene’s view controller.

When you release your mouse button, a Storyboard Segues box appears, as shown in Figure 11.8. Here you can choose the type of segue that you’re creating, most likely Present Modally or Popover Presentation.

Image

FIGURE 11.8 Choose the segue type to create.

A total of five options appear:

Image Show: Create a chain of scenes where the user can move forward or back. This is used with navigation view controllers, which we look at in Hour 13.

Image Show Detail: Replace the current scene with another. This is used in some view controllers, such as popular the split-view controller. We’ll look at this in Hour 14, “Navigating Information Using Table Views and Split View Controllers.”

Image Present Modally: Transition to another scene for the purposes of completing a task. When finished, we dismiss the scene, and it transitions back to the original view. This or the Popover Presentation is the segue you’ll use most often.

Image Popover Presentation: Displays the scene in a pop-up “window” over top of the current view on some devices (such as the iPad) or as a sliding modal view on others (iPhone).

Image Custom: Used for programming a custom transition between scenes.

For most projects, you’ll want to choose a modal or popover transition, which is what we use here. The other segues are used in very specific conditions and do not work unless those conditions are met. If that piques your interest, good; you’ll see more of these over the next few hours.


Note: Adaptive Segues

The segues listed here are known as adaptive segues in iOS 8. That means that they’ll adapt to whatever platform they’re running on. While you’ve been targeting iPads or iPhones independently, this “adaptive” nature will make creating universal applications much easier later in the book (and, to be honest, later in this hour—but that’s a surprise).

You’ll see additional segues listed as being “deprecated” when linking your view controllers. These are the “old” (nonadaptive) way of doing things and should be avoided for your shiny new apps.



Tip

You can create a segue that isn’t attached to any particular UI element by Control-dragging from one scene’s view controller to another. Doing so creates a segue that you can trigger, in your code, from a gesture or other event.


After adding the segue to your project, you’ll see a line added to the editor area that visually ties your two scenes together. You can rearrange the individual scenes within the editor to create a layout that maps how the application will flow. This layout is solely for your benefit; it doesn’t change how the application will operate.

You’ll also notice a representation of it in your document outline. The scene that is initiating a segue will show a new line “<Segue name> to <destination>” in the outline. Selecting the segue line gives us the opportunity to configure an identifier (seen in Figure 11.9) and several other settings.

Image

FIGURE 11.9 Set an identifier or change the type of segue being used.

The identifier is an arbitrary string that you can use to trigger a segue manually or identify which segue is underway programmatically (if you have multiple segues configured). Even if you don’t plan to use multiple segues, it’s a good idea to name this something meaningful (toEditor, toGameView, and so on).

The Segue drop-down appears on any segue you’ve added and enables you to switch between segue types at will. Depending on the type of segue you’ve chosen, you’ll have a few additional options to set. Let’s start with the most common type: modal segues.

Configuring Modal Segues

Modal segues offer many different settings for controlling the appearance of the destination view controller as it is displayed on the screen. When viewing a modally presented segue in the Attributes Inspector, you’ll see two drop-downs—presentation and transition, visible in Figure 11.10. These options can dramatically alter how your scenes appear to the user.

Image

FIGURE 11.10 Configure each segue you add.

As mentioned earlier, segues adapt to the device the application is running on. At present, most of the presentation settings will only result in a display difference on the iPad. The iPad has more screen real estate than an iPhone, so it can do things a little differently. You have four presentation style options:

Image Full Screen: Sizes the view so that it covers the full screen.

Image Current Context: Uses the same style display as the scene that is displaying it.

Image Page Sheet: Sizes the scene so that it is presented in the portrait format.

Image Form Sheet: Sizes the scene smaller than the screen (regardless of orientation), showing the original scene behind it. This, for all intents and purposes, is the equivalent of a window.

The transition type is a visual animation that is played as iOS moves from one scene to another. You have four options here (as shown in Figure 11.10):

Image Cover Vertical: The new scene slides up over the old scene.

Image Flip Horizontal: The view flips around horizontally, revealing the new scene on the “back.”

Image Cross Dissolve: The old scene fades out while the new scene fades in.

Image Partial Curl: The old scene curls up like a piece of paper, revealing the new scene underneath.


Caution: Choose Your Styles Carefully!

Not all styles are compatible with all transitions. A page curl, for example, can’t take place on a form sheet that doesn’t completely fill the screen. Attempting to use an incompatible combination will result in a crash. So if you’ve chosen a bad pair, you’ll find out pretty quickly (or you could review the documentation for the transition/style you plan to use).


The default transition—cover vertical—presents the new view by sliding it up over the initial view. This is perfectly acceptable, but I encourage you to try the other transitions for some nifty interactive effects. The partial curl, for example, can be moved with a finger to reveal more content, as demonstrated in Figure 11.11.

Image

FIGURE 11.11 Modally presented segues offer a wide range of appearance options.

Configuring the Popover Segue

Popovers are variation on modal segues that display content on top of an existing view, with a small indicator that points to an onscreen object, such as a button, to provide context. Popovers are everywhere in the iPad interface, from Mail to Safari, as demonstrated in Figure 11.12.

Image

FIGURE 11.12 Popovers are everywhere in the iPad UI.

Using a popover enables you to display new information to your users without leaving the screen you are on, and to hide the information when the user is done with it. There are few desktop counterparts to popovers, but they are roughly analogous to tool palettes, inspector panels, and configuration dialogs. In other words, they provide UIs for interacting with content on the screen, but without eating up permanent space in your UI.

What makes a popover different from a modally presented view is that it also requires an additional controller object, a popover controller (UIPopoverPresentationController). The controller determines the source view of the popover and where it points. When the user is done with the popover, touching outside of its visible rectangle automatically closes the view.

Preparing Popovers

To create a popover, follow the exact same steps as when creating a modally presented segue. Control-drag from the element you want to display a popover to the view controller providing the popover content. When prompted for the type of storyboard segue, as shown in Figure 11.13, choose Popover Presentation.

Image

FIGURE 11.13 Set the segue type to Popover Presentation.

What makes the popover presentation attractive for any project is that when you create a popover segue and deploy it to the iPhone, it will be presented as a modally presented segue. The adaptive nature of iOS segues mean that the proper interface conventions are followed regardless of the platform. When will this come in handy? When you begin creating applications that run on both iPhones and iPads—which (hint hint) might be sooner than you think.

Setting the Popover Size

The default view associated with a new iPad scene is the same size as the main application interface. When you are displaying a popover, however, the scene needs to be much smaller. Apple allows popovers up to 600 points wide, but recommends that they be kept to 320 points or less. To set the size of the popover, select the view within the popover view controller, and open the Size Inspector (Option-Command-5). Use the Width and Height fields to enter a size for the popover. After you set the size of the view, the scene’s visual representation in the IB editor changes to the appropriate size, as shown in Figure 11.14. This makes building the content view much easier.

Image

FIGURE 11.14 Edit the height and width of the popover view.


Caution: Can’t Set Your Popover Size?

If you find yourself looking at a dimmed-out size setting for the popover view, you probably haven’t yet created the popover segue. It isn’t until Xcode “knows” that you’re adding a popover scene that it unlocks the size settings.


Configuring the Presentation Directions and Passthrough Views

After setting the popover’s size, you want to configure a few attributes on the segue itself. Select the popover segue within the initiating scene, and then open the Attributes Inspector (Option-Command-4), as shown in Figure 11.15.

Image

FIGURE 11.15 Configure the popover’s behavior by editing the segue’s attributes.

Within the Storyboard Segue settings, start by setting an identifier for the popover segue. Providing an identifier makes it possible to invoke the popover programmatically, something we look into shortly. Next, choose the directions that the popover’s arrow will appear from; this determines where iOS will present the popover on the screen.

For example, if you only allow a presentation direction of left, the popover displays to the right of whatever object is invoking it.

When a popover is displayed, touching outside of it makes it disappear. If you want to exclude certain UI elements from dismissing the popover, just drag from the Passthrough field to those objects in your view.


Note

By default, a popover’s “anchor” is set when you Control-drag from a UI object to a view controller. The anchor is the object that the popover’s arrow will point to.

As with the modal segue covered earlier, you can create “generic” popover segues that aren’t anchored. Control-drag from the originating view controller to the popover content view controller and choose a popover segue when prompted. We discuss how to display one of these generic popover segues from any button in a few minutes.


That’s all you need to do to create a working popover in IB. Unlike a modal view, a popover is automatically dismissed when you touch outside of it, so you don’t even need a single line of code to create a working interactive popover.

After setting the identifier, style, transition, and presentation for a segue, you’re ready to use it. Without you writing any code, an application that has followed these steps can now present two fully interactive views and transition between them. What it cannot do, however, is interact with them programmatically. In addition, once you transition from one view to another, you cannot transition back. For that, you need some code. Let’s take a look at how you can create and trigger modal segues programmatically, and then the different ways of transitioning back—all of which require some coding.

Presenting Modal Segues Manually

Although it is easy to create segues with a single Control-drag, in several situations you have to interact with them in programmatically. If you create a segue between view controllers that you want to trigger manually, for example, you need to know how to initiate it in code. When users are done with the task in another scene, they also need a mechanism to dismiss the modal scene and transition back to the original scene. Let’s handle these scenarios now.

Starting the Segue

First, to transition to a scene using a segue that you’ve defined in your storyboard, but don’t want to be triggered automatically, you use the UIViewController instance method performSegueWithIdentifier:sender. For example, within your initial view controller, you can initiate a segue with the identifier "toMyGame" using the following line:

performSegueWithIdentifier("toMyGame", sender: self)

That’s it. As soon as the line is executed, the segue starts and the transition occurs. The sender parameter should be set to the object that initiated the segue. (It doesn’t matter what that object is.) It is made available as a variable property during the segue if your code needs to determine what object started the process.

Dismissing a Modal Scene Programmatically

After users have finished interacting with your view, you’ll probably want to provide them with a means of getting back to where they started. At present, there is no facility in modal segues to allow for this, so you must turn to code. The UIViewController method dismissViewControllerAnimated:completion can be used in either the view controller that displayed the modal scene or the modal scene’s view controller to transition back to the original scene:

self.dismissViewControllerAnimated(true, completion: nil)

The completion is an optional closure that will be executed when the transition has completed. You can learn more about closures in Hour 3, “Discovering Swift and the Xcode Playground,” and you should have used a few in the previous hour’s lesson. After you’ve dismissed a scene presented modally, control is returned to the original scene and the user can interact with it as she normally would.

What if you’ve performed several segues and want to jump back to where you started rather than just going back one? For that, you need to make use of exits and unwind segues.

Using Exits (and the Unwind Segue)

Apple has adopted the term unwinding to mean “moving backward in a storyboard.” Storyboards show the path a user takes forward through an application, but (until now) haven’t really shown a means of moving backward. In the preceding section, you learned how to move back to an earlier view controller using dismissViewControllerAnimated:completion. This will likely be the most common method you use for unwinding, but it’s hardly a flexible solution for jumping back to an arbitrary point in your storyboard.

If you’ve displayed 10 modal view controllers, one after the other, and you want to jump from the tenth back to the second, do you really need to dismiss each view controller from number 10 back to number 2? Not if you make use of exits and the unwind segue.

Preparing a View Controller for an Exit

To use an exit, you must first decide what view controller should allow exits. This is the view controller for the scene that you want to exit to not from. Although this seems a bit counterintuitive to me, just remember that you implement the exit as your destination.

After you’ve made that determination, add a new IBAction method, shown in Listing 11.1, in the view controller’s swift file.

LISTING 11.1 Setting an Exit Point


@IBAction func exitToHere(sender: UIStoryboardSegue) {
    // No code needed!
}


There are two unique things to recognize about this method. First, the name of the method can be anything you want, it just needs to have a single UIStoryboardSegueparameter. Second, you don’t have to add any implementation code to the method. It can remain entirely empty.

Once the method is in place, you can use the Exit icon in your scene.

Connecting to an Exit (Unwind Segue)

To connect to an exit, you follow almost the same process as creating a segue. First, you need something that will trigger the exit (like a button). Control-drag from that object to the Exit icon in the scene you want to exit to, as demonstrated in Figure 11.16.

Image

FIGURE 11.16 Connect to the exit.

When you release your mouse button, you’ll be asked to pick from a list of the available exit/unwind methods; choose the exit/unwind method you implemented. You’ll notice that a new unwind segue is added to the scene you are transitioning from, as shown in Figure 11.17.

Image

FIGURE 11.17 Dragging to the Exit icon creates an unwind segue.

Once the segue is in place, activating the segue will jump from the activating view controller to the controller with the exit. You can work with the exit/unwind segues exactly like any other segue—including setting an identifier, creating a manual segue (by dragging from a view controller rather than a GUI element), and executing the unwind segue programmatically.


Dynamically Determining the Unwind Destination

Unwinding segues can be quite a bit more complex than we can present here. You can, for example, implement custom view controllers that, within a hierarchy of views, dynamically decide whether they will become the destination of an unwind segue via the method canPerformUnwindSegueAction:fromViewController:withSender.

By overriding this method, your view controller can look at the view controller that wants to initiate an unwind, then return true or false to accept the unwind request. If it returns false, the initiating view controller can keep looking.


Programming a Segue from Scratch

Xcode storyboarding has made multiscene applications much easier to create than they were in the past, but that doesn’t mean they’re the right choice for all your applications. If you’d rather go the route of programmatically presenting a scene without defining a segue at all, you certainly can. Let’s review the process.

Setting a Storyboard Identifier

After creating your storyboard scenes, but before coding anything, you must provide a storyboard identifier for the view controller you want to display programmatically. This is done by selecting the view controller instance and opening the Identity Inspector (Option-Command-3) in the IB editor. Within the Identity section of the inspector, use the Storyboard ID field to enter a simple string to identify the view controller within your application’s storyboard. Figure 11.18 shows a view controller being configured with the storyboard ID myEditor.

Image

FIGURE 11.18 Create a storyboard identifier for the view controller.

Instantiating the View Controller and View

To display a scene within a storyboard, your application will need to create a UIStoryboard object using the method storyboardWithName that references your storyboard file. This can be used to load view controllers and their associated views (that is, scenes).

For example, to create an object mainStoryboard that references the project’s Main.storyboard file, you could use the following:

let mainStorybord: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

Next, you configure the storyboard object to instantiate the view controller that you want to transition to using the instantiateViewControllerWithIdentifier method. Assume you’ve created a UIViewController subclass named EditorViewControllerand set the view controller storyboard identifier to "myEditor". You can instantiate a new instance of EditorViewController as follows:

let editorVC: EditorViewController =
mainStorybord.instantiateViewControllerWithIdentifier("myEditor"as
EditorViewController

The EditorViewController instance, editorVC, is now ready to be displayed. Before that happens, however, you may want to adjust how it will appear onscreen.

Configuring the Segue Style

Earlier I covered the different transition styles and presentation types that are available for displaying modal scenes. When displaying a view controller manually, you can apply the same effects programmatically by setting the modalTransitionStyle and modalPresentationStyle view controller variable properties, respectively. For example, to configure the editorVC view controller instance, I might use this:

editorVC.modalTransitionStyle=UIModalTransitionStyle.CoverVertical
editorVC.modalPresentationStyle=UIModalPresentationStyle.FormSheet

You can choose from the same transitions and presentation styles as mentioned earlier this hour, but you need to use these constants to identify your selections:

Image Transition styles: UIModalTransitionStyle.CoverVerticalUIModalTransitionStyle.FlipHorizontalUIModalTransitionStyle.CrossDissolve, or UIModalTransitionStyle.PartialCurl

Image Presentation styles: UIModalPresentationStyle.FormSheetUIModalPresentationStyle.PageSheetUIModalPresentationStyle.FullScreenUIModalPresentationStyle.CurrentContextUIModalPresentationStyle.OverFullScreenUIModalPresentationStyle.OverCurrentContext, or UIModalPresentationStyle.Popover

Notice that one of the presentation styles is UIModalPresentationStyle.Popover? If you set the modalPresentationStyle to this value, you’ve effectively configured a popover segue. There are a few additional attributes you can use to choose how the popover is displayed; we’ll get to those in a few minutes.

Displaying the View Controller

The final step in programmatically displaying a view is to, well, display it. To do this, use the UIViewController method presentViewController:animated:completionfrom within your application’s initial view controller:

presentViewController(editorVC, animated: true, completion: nil)

The view controller and its associated scene are displayed on the screen using the transition and presentation styles you’ve selected. From here out, you can work with the scene as if it were one you displayed via a segue. You dismiss it using the same dismissViewControllerAnimated:completion method:

dismissViewControllerAnimated(true, completion: nil)


Note

In this example, we’re programmatically creating a segue to a scene. The methods we use to do this, however, refer to view controllers. Keep in mind that a scene is just a view controller and its associated view. Because we’re instantiating a view controller (with an associated view) from the project’s storyboard, we’re effectively instantiating a “scene.” We then configure the presentation of the view controller/view and display it (the same as a segue).

Although the terminology shifts when working in code, the end result is the same.


Popover Peculiarities

In the previous section, you learned how to create a modal segue (and even configure it as a popover) programmatically—but there are still some peculiarities that you must address when working with popovers. When creating one in Interface Builder, for example, you defined the direction of the popover “arrow” and what object it should point to. To do the same in code, you need to access the UIPopoverPresentationController—a special object that defines characteristics about the popover’s appearance. When you manually set options for the arrow direction and so on for a popover segue in Interface Builder, you’re actually configuring a UIPopoverPresentationController that is automatically created for you.

Let’s get back to the example of an “editor” view controller that we want to present modally. Previously we covered the steps to display it as a modal view, but if we want to configure it as popover, we might start with this code:

let editorVC: EditorViewController =
mainStorybord.instantiateViewControllerWithIdentifier("myEditor"as
EditorViewController
editorVC.modalPresentationStyle=UIModalPresentationStyle.Popover

Once we’ve setup the view controller (editorVC) and set the presentation style to a popover, we can access a UIPopoverPresentationController that iOS automatically creates for us via the variable property - popoverPresentationController:

let presentationController:UIPopoverPresentationController =
     editorVC.popoverPresentationController!

With access to the presentation controller, we can now control several aspects of the popover display and dismissal process.

Setting the Popover Arrow

To finish the popover presentation, we must determine a few things about our display. First, what object is the popover going to presented from? Any object that you add to a view is a subclass of UIView, which has a bounds variable property. Popovers are easily configured to appear from the rectangle determined by an object’s bounds; as long as you have a reference to the object displaying the popover, you’re set. If you’re triggering the popover from a UI action, the bounds property of the object that triggered the action, for example, is retrieved with this: (sender as UIView).bounds.

Although Apple’s documentation states that we only need a rectangle or a UIView(pretty much any onscreen object) to configure the source for a UIPopoverPresentationController, in practice it does not work. I lamented this fact in the last hour when working with the iPad version of the Alert Sheet style alert. To set the object that the popover will be displayed from, you must set both a sourceRectand a sourceView.

Assuming the object you’re presenting is stored in the sender variable of an IBAction, you might use the following:

presentationController.sourceRect = (sender as UIView).bounds
presentationController.sourceView = sender as UIView


Note

You could certainly cast the sender as the object it really is (such as a UIButton—we did this in the last hour), but this implementation gives us the flexibility to have any UI object trigger an action and grab its frame value.


Then, we have determined the popover’s presentation direction. Do this by choosing from these constants:

Image UIPopoverArrowDirection.Any: The popover can be presented in any direction, giving iOS the greatest flexibility in determining how the popover is displayed.

Image UIPopoverArrowDirection.Up: The arrow is only displayed pointing up, meaning that the popover appears below the object.

Image UIPopoverArrowDirection.Down: The arrow is displayed pointing down, and the popover appears above the object.

Image UIPopoverArrowDirection.Left: The arrow is displayed pointing left, and the popover appears to the right of the object.

Image UIPopoverArrowDirection.Right: The arrow is displayed pointing right, and the popover appears to the left the object.

Apple recommends that whenever possible you should use the UIPopoverArrowDirection.Any constant. You can set the popover direction by assigning the constant (or multiple constants separated by a pipe (|)) to the UIPopoverPresentationController variable attribute permittedArrowDirections. For example, to present the editorVC popover with an arrow that points either up or down, you might use the following:

presentationController.permittedArrowDirections=
    UIPopoverArrowDirection.Up | UIPopoverArrowDirection.Down

A final display parameter that you need if creating and presenting a popover manually is to set the content size of the popover. This is not set on the presentation controller; it is set by assigning the preferredContentSize variable property on the popover’s view controller. To set a popover size of 320 points wide and 400 points tall, you could type the following:

preferredContentSize = CGSizeMake(320.0400.0)

With the size set, the arrow directions configured, and the source location chosen, you can proceed to present the view with the presentViewController method (just like a “plain” modal segue).

There’s still one more thing that we need to chat about with regards to popover presentation. When a popover is presented, a user can dismiss it by touching outside of it. This isn’t tied to an IBAction, so how can we get ahold of the event of the user dismissing a popover? The answer is through implementing the UIPopoverPresentationControllerDelegate protocol.

Implementing the UIPopoverPresentationControllerDelegate Protocol

When I first started developing on Apple platforms, I found the terminology painful. It seemed that no matter how easy a concept was to understand, it was surrounded with language that made it appear harder than it was. A protocol, in my opinion, is one of these things.

Protocols define a collection of methods that perform a task. To provide advanced functionality, some classes, such as UIPopoverPresentationController, may require you to implement methods defined in a related protocol to add certain functionality. Doing this is called conforming to the protocol. Some protocol methods are required and others are optional; it just depends on the features you need.

To deal with the user dismissal of a popover, the class that is responding to the dismissal (usually just a view controller) should conform to the UIPopoverPresentationControllerDelegate protocol.

To declare that a class, such as a view controller, will be conforming to the UIPopoverPresentationControllerDelegate protocol, you just modify the class line in the swift file as follows:

class ViewController: UIViewControllerUIPopoverPresentationControllerDelegate {

Next, you must set the delegate of the popover presentation controller to the object implementing the protocol. If this is the same object that is creating the popover, and you already have a copy of the presentation controller in presentationController, you can just use self, as follows:

presentationController.delegate=self

Now, when the popover is dismissed, the method popoverPresentationControllerDidDismissPopover will be called from popover’s view controller. All that remains is to implement that method, as demonstrated in in Listing 11.2.

LISTING 11.2 Handling a Popover Dismissal


func popoverPresentationControllerDidDismissPopover(popoverPresentationController:
    UIPopoverPresentationController) {
    // Handle any actions you want executed here.
}


As you can see, it isn’t difficult to work with popovers programmatically, but a bit more setup is required.

Passing Data Between Scenes

You know how to create and display scenes, but there is one very critical piece of the puzzle missing: the ability to share information between the different scenes in your application. Right now, they act as entirely independent applications, which is perfectly fine if that is your intention; however, chances are, you want an integrated user experience. Let’s make that happen.

The most straightforward way for any class to exchange information with any other is through its variable properties and methods. The only trouble with this is that we need to be able to get an instance of one scene’s view controller from another, and, at present, when using a segue we create visually, this process isn’t entirely obvious.


Tip

If you create and display a scene entirely programmatically, as demonstrated in the preceding section, you already have an instance of the new scene’s view controller in your initial view controller. You can set/access variable properties on the new view controller (editorVC.myImportant-VariableProperty=<value>) before displaying it and after it is dismissed.


The prepareForSegue:sender Method

One way to get references to the view controllers in a segue is by overriding UIViewController prepareForSegue:sender method. This method is automatically called on the initiating view controller when a segue is about to take place away from it. It returns an instance of UIStoryboardSegue and the object that initiated the segue. The UIStoryboard object contains the variable properties sourceViewController and destinationViewController, representing the view controller starting the segue (the source) and the view controller about to be displayed (the destination).

Listing 11.3 shows a simple implementation of this approach. In this example, I’m transitioning from my initial view controller (an instance of ViewController) to a new view controller, which is an instance of a hypothetical EditorViewController class.

LISTING 11.3 Using prepareForSegue:sender to Grab the View Controllers


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    let startingViewController:ViewController =
        segue.sourceViewController as ViewController

    let destinationViewController:EditorViewController =
        segue.destinationViewController as EditorViewController
}


In this implementation, I declare two constants (startingViewController and destinationViewController) to reference the source and destination controllers. Then, I assign them to typecast versions of the source and destination variable properties returned by the UIStoryboardSegue. I have to typecast the view controllers so that Xcode knows what type of object they are; otherwise, I wouldn’t be able to access their variables and methods. Of course, the source view controller is also just self, so this is a bit of a contrived example.

Once we have a reference to the destination view controller, however, we can set and access variable properties on it, even changing the presentation and transition styles before it is displayed. If it is assigned to a variable property, it can be accessed anywhere within the source view controller.

What if we want the destination view controller to send information back to the source? In this case, only the source can communicate with the destination, because that’s where the prepareForSegue:sender method is implemented. One option is to create a variable property on the destination controller that stores a reference to the source controller. Another approach, however, is to use built-in variable properties of UIViewController that make working with modally presented scenes easy, easy, easy.


It’s Not Just for Getting the Controllers!

The prepareForSegue:sender isn’t just for getting the view controllers involved in a segue; it can also be used to make decisions during a segue. Because a scene can define multiple different segues, you might need to know which segue is happening and react accordingly. To do this, use the UIStoryboardSegue variable property identifier to get the identifier string you set for the segue:

   if segue.identifier=="myAwesomeSegue" {
       // Do something unique for this segue.
}


The Easy Way

The prepareForSegue:sender gives us a generic way to work with any segue that is taking place in an application, but it doesn’t always represent the easiest way to get a handle on the view controllers involved. For modal segues, the UIViewControllerclass gives us variable properties that make it easy to reference the source and destination view controllers: presentingViewController and presentedViewController.

In other words, we can reference the original (source) view controller within a view controller that has just been displayed by accessing presentingViewController. Similarly, we can get a reference to the destination view controller from the original controller with presentedViewController. It’s as easy as that.

For example, assume that the original view controller is an instance of the class ViewController, and the destination view controller is an instance of EditorViewController.

From the EditorViewController, you can access variable properties in the original view controller with the following syntax:

(presentingViewController as ViewController).<variable property>

And within the original view controller, you can manipulate variable properties in the destination view controller with this:

(presentedViewController as EditorViewController).<variable property>

The parentheses with the class name is necessary to typecast presentingViewController/presentedViewController to the right object types. Without this notation, Xcode wouldn’t know what types of view controllers these were, and we wouldn’t be able to access their variable properties.

With this data-passing knowledge under our belts, we can go ahead and build our first multi-scene application—and there’s even a surprise—you’re going to make it work on both iPhone and iPad devices.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值