Chapter    3

Structure Your App

To implement your software, you make design decisions based on how you’d like to structure your app in terms of organizing your code. To decide your iOS app structure up front, the top-down approach and model-view-controller (MVC) design pattern are recommended and actually embedded in the iOS software development kit (SDK) and tools. MVC may not be enforced in most of the popular web development platforms, but the vocabularies should be familiar to both front-end and back-end web developers. The techniques may vary, but the principle is the same: structure your app into three software layers consisting of the model, view, and controller.

In the iOS programming paradigm, you are encouraged to take the so-called top-down approach for creating your mobile apps, where you design the application workflow prior to detailing each individual screen. Xcode will guide you to turn on your top-down programming thinking hat.

I will discuss MVC first, followed by how to create the iOS storyboard in Xcode. With the guided screen navigation patterns, your iOS storyboard naturally breaks your iOS apps into MVC components to form the skeleton of the iOS mobile app.

Model-View-Controller

WEB DEV ANALOGY

Perhaps it is best not to try to compare the iOS MVC design pattern with any web MVC design patterns. For those who have worked in web development for a long time, you probably have seen many MVC framework variants. All you need to know now is there is only one MVC in iOS, and it is very clean compared to the various web MVC framework libraries.

MVC breaks the graphical user interface (GUI) app into three layers. The iOS MVC design pattern specifies that a GUI application consists of a data model, a presentation view, and controller layers, as shown in Figure 3-1.

9781484209325_Fig03-01.jpg

Figure 3-1. The iOS MVC design pattern

In the iOS SDK, you explicitly use the MVC vocabularies of Content View and Content View Controller. You naturally break down your iOS app into MVC classes, starting with creating a storyboard prototype using the Xcode storyboard editor.

Unless your app has only one screen, you need to decide how to implement navigation and screen transitions among multiple view controllers. You need an optional MVC participant: a Container View Controller to coordinate the view controllers. In iOS, the SDK provides several Container View Controllers for screen navigation purposes; you simply choose the appropriate Container View Controller class and let the iOS framework facilitate the tasks for you.

I’ll start with the Content View and Content View Controller and then talk about the Container View Controller.

Content View

WEB ANALOGY

The Content View is equivalent to the web presentation layer. It could be a static HTML page, server-side template, or client-side template. The technique may be different, but the principle is the same: you create a presentation layer that is not tightly coupled to the data.

A Content View provides a visible area so that users can interact with the app. The Content View defines how to render itself with content and can interact with user actions. To create Content Views in iOS, use an Xcode storyboard.

When it comes down to implementing a Content View, you face the same issue you normally encounter when implementing a web page: how to make the page rendering adaptive to screen size. This is actually a hot topic in web development as well. Recall that in the iOS HelloMobile app, you drew the UI widgets in the storyboard scene by dragging and dropping the widgets to draw them on the parent view. However, this iOS Content View is not adaptive to other device types and screen orientations yet. You can easily observe the landscape problem in the Assistant Editor previews (for example, see Figure 2-14 in Chapter 2). Creating adaptive Content Views for various screen sizes is a common task in many platforms. In iOS, you essentially use iOS platform features for the same purposes.

  • Auto Layout: This works best with responsive user experience (UX) designs that are agnostic/adaptive to screen sizes.
  • Size classes: These provide ultimate flexibility for customizing the screens for different screen sizes.

Auto Layout

You use iOS Auto Layout to position each UI widget by aligning or spacing it relative to other widgets, that is, siblings or the parent view.

The three widgets in the current iOS HelloMobile project are not positioned properly in landscape mode (see Figure 2-14 in Chapter 2). Here you’ll use iOS Auto Layout to fix this while learning its uses.

While extremely powerful, some Xcode editors and operations are collapsed in the menus and can be difficult to find for a beginner. Figure 3-2 depicts some quick tips.

  • If you cannot find an editor or navigator, look in the View menu in Xcode’s top menu bar.
  • Auto Layout operations are grouped in the Editor menu in the Xcode top menu bar.
  • There are four small buttons on the bottom toolbar of the storyboard editor. They offer quick Auto Layout operations and give you some visual hints of what they are.
  • Assistant Editor is an important frequent used feature, that you will use next.
  • The view selector in the Utility area allows you to switch between several inspectors. You will use inspectors a lot in the next chapter, too.

9781484209325_Fig03-02.jpg

Figure 3-2. Storyboard Auto Layout operations in Xcode

Continue working on the HelloMobile project. Do the following:

  1. The storyboard preview in Assistant Editor is useful for immediately seeing any changes for the selected storyboard scene.
    1. Select the main.storyboard file and open the Assistant Editor (see the right pointer in Figure 3-3).
    2. Select the Preview option from  the Assistant menu selector that is default to “Automatic” (see the left pointer in Figure 3-3).

    9781484209325_Fig03-03.jpg

    Figure 3-3. Two steps to reach the Xcode storyboard preview

  2. Center the TextField horizontally in the container to create an x-alignment constraint, as shown in Figure 3-4.
    1. Select the TextField in the storyboard editor.
    2. In the Xcode top menu bar, select Editor image Align image Horizontal Center in Container.

    9781484209325_Fig03-04.jpg

    Figure 3-4. Using Horizontal Center in Container on the TextField in the Auto Layout editor

  3. Use Vertical Center in Container to create a y-alignment constraint (see Figure 3-5).
    1. From the Xcode top menu bar, select Editor image Align image Vertical Center in Container.
    2. In the Attributes Inspector in the Utility area, to position the TextField at the one-sixth of the view height instead of half, you can apply a multiplier of 3.

    9781484209325_Fig03-05.jpg

    Figure 3-5. Creating a y-alignment constraint using a multiplier

     Note  Use Multiplier or Constant to create an offset for the second item position:

    (first item position) == (second item) * multiplier + constant

  4. Select Resolve Auto Layout Issues image Update Frames, as shown in Figure 3-6.

    9781484209325_Fig03-06.jpg

    Figure 3-6. Using Update Frames to reposition the UI widgets based on constraints

  5. Select the Hello World! label and click the Pin button to add multiple constraints for appropriate spacing and widgets height, as shown in Figure 3-7.
    1. Pin the Top space to the nearest widget, the TextField, with 48.5 pixels.
    2. Pin both the Leading and Trailing spaces to the nearest widget, the parent view of the label, with 60 pixels.
    3. Pin the label’s Height to be 21 pixels.

    9781484209325_Fig03-07.jpg

    Figure 3-7. Spacing Hello World! label relative to its neighbors

     Note  The combined constraints need to make sense without ambiguity. You can set a priority for each individual constraint. However, if you start using priorities to resolve conflicts, you might want to think about using fewer constraints.

  6. Select the Hello World... button. You can align it similar to the TextField.
    1. Use Horizontal Center in Container to create an x-alignment constraint.
    2. Use Vertical Center in Container to create a y-alignment constraint with a multiplier of 0.75.

Using Auto Layout with responsive UX designs immediately provides the proper landscape layout. Figure 3-8 shows the previews in both landscape and portrait modes for iPhone 4-inch and 3.5-inch modes. Click the + icon (see the pointer in Figure 3-8) to add multiple previews for different devices.

9781484209325_Fig03-08.jpg

Figure 3-8. Auto Layout with responsive UX design

Size Classes

WEB ANALOGY

Responsive web design would use media query breakpoints in the style sheet to target specific screen sizes.

While Auto Layout provides an effective way to implement responsive UX for various screen sizes, it may not utilize the valuable mobile-screen real estate in the most efficient way. For example, it is fairly common to lay out landscape view differently from portrait because of the different aspect ratios to have tablet-specific UX design, and so forth.

Prior to iOS 8, you generally implemented two storyboards, one for iPhone and one for iPad. Beginning with iOS 8, size classes were introduced to solve this common programming issue by using the abstract presentations of device sizes in terms of horizontal widths and vertical heights. The current iOS devices can be classified as shown in Table 3-1.

Table 3-1. iOS Device Size Classes

Size Classes

Compact Width

•   iPhone: portrait width

•   iPhone: landscape width

Regular Width

•   iPad: portrait width

•   iPad: landscape width

Compact Height:

•   iPhone: landscape height

iPhone in landscape

Customized view controller

Regular Height:

•   iPad: portrait height

•   iPad: landscape height

•   iPhone: portrait height

iPhone in portrait

iPad in portrait

iPad in landscape

You can provide the entire implementation for all size classes all in one storyboard!

Recall the iOS HelloMobile app—it works only in iPhone portrait mode (see Figure 2-14 in Chapter 2), and it disables size classes. In the following steps, you will enable size classes to see how to use them:

  1. As shown in Figure 3-9, select Use Size Classes (bottom pointer) in the File Inspector (accessed as shown by the top two pointers).

    9781484209325_Fig03-09.jpg

    Figure 3-9. Selecting Use Size Classes

  2. Use the Assistant Editor to preview the iPhone and iPad screens. Size classes can be overwhelming in the beginning, but I have found the previews helpful (see Figure 3-10).
    1. The scenes are converted to the most adaptive size class: (wAny hAny). The Auto Layout constraints are also preserved in this size class. You immediately get the iPad scene working as expected.

    9781484209325_Fig03-10.jpg

    Figure 3-10. Size classes preview

  3. Click the size class control to select the size class (see Figure 3-11).
    1. Hover your mouse to see the highlight and title changes. Comparing this with Table 3-1, you can select the Any row or column that targets the wider size classes. The default is Any Width | Any Height, which is applied to all the size classes to start with.

    9781484209325_Fig03-11.jpg

    Figure 3-11. Using the size class selector to select a specific size class

  4. To provide a specific layout for an iPhone landscape scene, select the Compact Width | Compact Height, as shown in Figure 3-12.

    9781484209325_Fig03-12.jpg

    Figure 3-12. Compact Width | Compact Height for iPhones in landscape

  5. To demonstrate the powerful size class feature, start fresh for the compact-compact size class. From the top menu bar, select Editor image Resolve Auto Layout Issues image Clear Constraints in View Controller. This clears the Auto Layout constraints only in the selected size class; you can see the constraints still there, but they are grayed out.
  6. Drag the widgets to reposition them to get a quick idea—you don’t need to be precise (see Figure 3-13).
    1. Since you are providing a custom layout explicitly for iPhones in landscape, you can draw the positions precisely and let the storyboard editor do the rest by choosing Editor image Resolve Auto Layout Issues image Reset to Suggested Constraints in View Controller.
    2. If you tried the preceding step, select Clear Constraints in View Controller again to have a clean start for creating Auto Layout constraints.

    9781484209325_Fig03-13.jpg

    Figure 3-13. Two-sided view for compact height (iPhone landscape mode)

  7. For the TextField, add the following Auto Layout constraints:
    1. Use Horizontal Center in Container to create an x-alignment constraint with a multiplier of 2.
    2. Use Vertical Center in Container to create a y-alignment constraint with a multiplier of 1.5.
    3. From the top menu bar, select Update Frame at Editor image Resolve Auto Layout Issues.
    4. To update the existing constraints, either select the constraint from the storyboard navigator or select the widget on the scene first to see and click the guided line in the storyboard scene. Use the Attributes Inspector in the Utility area (see Figure 3-14) to update any constraint attribute.

    9781484209325_Fig03-14.jpg

    Figure 3-14. Updating the Auto Layout constraint

  8. For the Label object, add the following Auto Layout constraints in the same way as in step 7:
    1. Use Horizontal Center in Container to create an x-alignment constraint with a multiplier of 2.
    2. Use Vertical Center in Container to create a y-alignment constraint with a multiplier of 0.75.
    3. From the top menu bar, select Update Frame at Editor image Resolve Auto Layout Issues.
  9. For the Button object, add the following Auto Layout constraints in the same way as in step 7:
    1. Use Horizontal Center in Container to create an x-alignment constraint with a multiplier of 0.67.
    2. Use Vertical Center in Container to create a y-alignment constraint with a multiplier of 1.
    3. From the top menu bar, select Update Frame at Editor image Resolve Auto Layout Issues.

All the device classes in previews look good, as expected (Figure 3-15).

9781484209325_Fig03-15.jpg

Figure 3-15. Device classes previewed in the storyboard editor

You can run the app in all emulators to see the work in action. Figure 3-16 shows the iPhone 4s emulator. The framework takes care the transition animations for you, too!

9781484209325_Fig03-16.jpg

Figure 3-16. iPhone 4s portrait and landscape size classes

Content View Controller

WEB ANALOGY

This is the code that is responsible for getting data and injecting it into the template or Document Object Model (DOM) elements.

The Content View Controller participant pairs with a Content View (see Figure 3-1). In the iOS, the Content View normally is created statically in the storyboard. The Content View Controller class manages the Content View to present the dynamic behavior of the user interface by conveying information to and interacting with users. In iOS, you create a custom class by subclassing from UIViewController.

Your primary Content View Controller tasks are as follows:

  • Pair with its own Content View
  • Keep object references to the UI widgets in the Content View
  • Implement methods to respond to widget events

In iOS, you normally use the storyboard editor to connect the UI widgets or events to your code to facilitate these common Content View Controller programming tasks.

Pair with Content View

In iOS, you normally create a storyboard for your apps first (like the iOS HelloMobile project). Generally, for every storyboard scene (Content View), you create a Swift class subclassed from UIViewController to pair with it.

The iOS HelloMobile project is not completed yet; it renders only the initial screen but does not do anything when you click the Hello... button. You need a functional Content View Controller that can fulfill this responsibility. The Single View Application template already pairs a controller class for you: ViewController.swift. To demonstrate the whole subject, don’t use this class; instead, do the following to create your own class:

  1. Create a new file for a new Swift class.
    1. Right-click the HelloMobile folder in the Navigator area and then select New File image iOS image Source image Swift File (see Figure 2-3).
    2. Save the file as HelloViewController.swift.
    3. Create the HelloViewController class subclassed from UIViewController, as shown in Listing 3-1.

    Listing 3-1. HelloViewController Class Skeleton

    import UIKit
    class HelloViewController: UIViewController {
      // TODO
    }
  2. Pair the storyboard scene with the HelloViewController class (see Figure 3-17).
    1. Select Main.storyboard to open the storyboard editor.
    2. Select the view controller in the storyboard scene and open the Identity Inspector in the Utility area.
    3. Enter HelloViewController in the Custom Class field to pair the storyboard scene with the HelloViewController class.

    9781484209325_Fig03-17.jpg

    Figure 3-17. Identity Inspector to pair with the view controller

    Specifying the custom class in the Identity Inspector is all you need to pair with the Content View Controller.

    Interact with Content View

    WEB ANALOGY

    To interact with an element in a web app, you would attach an event listener, such as onClick(), which would call a function in your controller code.

    Generally speaking, you create UI widgets in a Content View, and your Content View Controller code updates the widget’s states or interacts with users at runtime. In iOS, you use the Connections Inspector to create IBOutlet and IBAction to facilitate this common programming task by drawing connections to your code in the Swift class.

    • IBOutlet: The view controller property that is connected to the widgets in the storyboard scene
    • IBAction: The view controller method that is called when the widget events occur

    The following walks you through the steps to connect the UI widgets and delegates action events to your controller class:

  3. Select Main.storyboard to open the storyboard editor.
    1. Open the storyboard Assistant Editor. The HelloViewController class should automatically open in the Assistant Editor.
    2. Sometimes the right file may not be opened automatically in the Assistant Editor, so you may need to select the right file manually (see the pointer in Figure 3-18).

    9781484209325_Fig03-18.jpg

    Figure 3-18. Selecting a file manually in the Assistant Editor

  4. Select the TextField in the storyboard scene (the left pointer in Figure 3-19) and open the Connections Inspector (the right pointer in Figure 3-19), as shown in Figure 3-19 (make sure the Utility area is unfolded).

    9781484209325_Fig03-19.jpg

    Figure 3-19. Opening the Connections Inspector

  5. Create an IBOutlet for the TextField in the storyboard scene (see Figure 3-20).
    1. Drag the circle next to New Referencing Outlet with three fingers (or hold the left trackpad button at the same time) and drop it inside the class. You should see the line from the circle, as shown in Figure 3-20.
    2. Enter the connection name (that is, mTextField). This creates a property in the Swift class.

    9781484209325_Fig03-20.jpg

    Figure 3-20. IBOutlet in the Connections Inspector

  6. Repeat steps 2 and 3 to create mLabel and mButton IBOutlets.
  7. Create an IBAction for the button touch-down events.
    1. Drag the circle next to the Touch Down in Sent Events section and drop it inside the Swift class (see Figure 3-21).
    2. Enter the method name, that is, onButtonTouchDown. This creates a method stub in the Swift class.
    3. Add the Say-Hello code to complete the IBActionmethod implementation in HelloViewController.swift, as shown in Listing 3-2.

9781484209325_Fig03-21.jpg

Figure 3-21. Creating an IBAction in the Connections Inspector

Listing 3-2. HelloViewController with IBOutlet and IBAction

import UIKit

class HelloViewController: UIViewController {
  
  @IBOutlet weak var mTextField: UITextField!
  @IBOutlet weak var mLabel: UILabel!
  @IBOutlet weak var mButton: UIButton!
  
  @IBAction func onButtonTouchDown(sender: AnyObject) {
    var str = mTextField.text
    mLabel.text = "Hello (str)!"
  }
}

This completes the whole HelloMobile iOS app. You can run the project in all iOS emulators to see the code in action.

You’re almost done with the MVC topics. Just one more small lecture before getting into more fun stuff: UIViewController life-cycle events.

UIViewController Life Cycle

WEB ANALOGY

DOM life-cycle events such as DOMContentLoaded, window.onload, or callbacks using jQuery.ready() are typical for web applications that need to manipulate the DOM. To detect when a page is being closed, the window.onbeforeunload event is used.

In iOS apps, life-cycle callbacks are called at various points when a Content View is being rendered. Certain tasks need to be performed in certain states to ensure the Content View is rendered smoothly. This applies to many modem UI platforms including iOS and Android. These events may not be a beginner topic, but the purpose is simple: you want to perform certain computing tasks at the right time to ensure the rendering process is smooth and data is ready at the right time.

Most of these events are optional to implement; you can choose to override these inherited system methods to receive timely callbacks if you want.

viewDidLoad

The iOS system calls the viewDidLoad() method when the view controller loads its Content View. You commonly put the initialization code here.

viewWillAppear

The iOS system calls the viewWillAppear() method when the view is about to appear. Compared to vewDidLoad(), this method gets called every time you revisit the view, while viewDidLoad() gets called only the first time.

viewDidAppear

The iOS system calls the viewDidAppear() method when the view becomes visible. If you want to show rendering effects upon the view being visited, that is, animation effects, you want to put the code in this method. On the other hand, you want to avoid time-consuming code in this method to avoid the UI not being responsive.

viewWillDisappear

The system calls the viewWillDisappear() method when the Content View is about to become invisible—for example, leaving for another storyboard scene. This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).

viewDidDisappear( )

The system calls the viewDidDisappear() method when the Content View is not visible.

When implementing these life-cycle events, you almost always want to call the corresponding super.viewXXX().

Screen Navigation Patterns

You commonly need multiple screens to convey hierarchical information and to interact with users. Considering the relatively small mobile screens, it is more crucial that you use well-known navigation patterns to make mobile apps more predictable. A consistent and predictable navigation pattern guides users to complete a task with multiple screens. Efficient navigation is one of the cornerstones of a well-designed mobile app.

Storyboard Segue

A segue (pronounced “seg-way”) is a type of a connection in a storyboard that specifies transitions from one scene to another. For instance, you can create an action segue that is performed immediately when the action is triggered. More frequently, you will create a manual segue in storyboard and write logic to perform the segue. Depending on its transition type, the segue may require a Container View Controller. For example, to implement the typical navigation stack transitions, you will need a Navigation Controller in iOS.

The following steps will walk you through creating a storyboard segue:

  1. Create a new Xcode project using the Single View Application template with a product name of Segues. (See Chapter 2 for step-by-step instructions.)
  2. Open Main.storyboard in the storyboard editor. It should look like Figure 3-22 when you’re done.
    1. Add two Button widgets to the existing scene: one for the action segue and the other for the manual segue.
    2. Drop two View Controller objects onto the storyboard from the Object Library to add two storyboard scenes. Add a UILabel to each scene with the titles From Action Segue and From Manual Segue.

    9781484209325_Fig03-22.jpg

    Figure 3-22. Segue preparation

  3. Create an action segue from the Action Segue button to the From Action Segue scene.
    1. Select the Action Segue button and open the Connections Inspector in the Utility area.
    2. Drag the action outlet in the Triggered Segue section to the From Action Segue view controller, as shown in Figure 3-23.
    3. Select Show for the transition type.

    9781484209325_Fig03-23.jpg

    Figure 3-23. Creating an action segue

  4. Select the segue (see the pointer in Figure 3-24) and enter the name of the segue identifier in the segue’s Attributes Inspector (that is, type actionSegue), as shown in Figure 3-24.

    9781484209325_Fig03-24.jpg

    Figure 3-24. Selecting the segue and setting up the attributes

  5. Create a Manual Segue from the presenting controller to the From Manual Segue view controller, as shown in Figure 3-25.
    1. Select the presenting view controller and open the Connections Inspector in the Utility area.
    2. Drag the circle (outlet) in the Manual Triggered Segue section to the From Manual Segue view controller.
    3. Select Show for the transition type.
    4. Select the segue and enter the name of the segue identifier in the segue’s Attributes Inspector (in other words, type manualSegue).

    9781484209325_Fig03-25.jpg

    Figure 3-25. Creating a manual segue

  6. Set up the manual segue to be performed programmatically when the Manual Segue button is clicked.
    1. To connect the button touch down events to your code, create an Touch Down IBAction to the ViewController class with the name onManualSegueTouchDown.
    2. In the onManualSegueTouchDown(...) method, use the code in Listing 3-3 to perform the manual segue.

Listing 3-3. Performing the Manual Segue

import UIKit
class ViewController: UIViewController {
  @IBAction func onManualSegueTouchDown(sender: AnyObject) {
    self.performSegueWithIdentifier("manualSegue", sender: sender)
  }
}

Run the app in different emulators to see these segues work in different size classes. Since iOS 8, the segues are presented in an adaptive manner to the size classes.

Pass Data with a Segue

WEB ANALOGY

Typically, web apps will send requests to the server by passing data through the URL query string, form posting, or cookies. This loads the entire page content for each user action. Single-page front-end apps can utilize animated screen transitions through asynchronous data loading or by front loading more data during their initialization.

The storyboard segues perform screen transitions easily by drawing the segue connections. You don’t even need a line of code for an action segue. However, you normally will need to pass data from the presenting view controller to the presented view controller, which cannot be done alone by the storyboard segue itself.

The following steps demonstrate the conventional iOS way to pass data from the presenting view controller to the presented view controller in the Xcode Segues project:

  1. Create a PresentedViewController class with a property to receive data. Listing 3-4 simply prints the received data in the viewDidLoad() method.
    1. Specify the PresentedViewController class in the Identity Inspector to pair with the From Action Segue storyboard scene (see the earlier Figure 3-17 for details).
    2. Pair the From Manual Segue storyboard scene with the PresentedViewController class.
    3. Add a property, data, to receive the data from the presenting view controller.

    Listing 3-4. Data Property to Receive Data from Presenting View Controller

    import UIKit
    class PresentedViewController: UIViewController {
      var data: String?
      override func viewDidLoad() {
        if let tmp = data {
          println("received data: (tmp)")
        }
      }
      ...
    }
  2. The system invokes a prepareForSegue(...) method in the source view controller. You need to implement this method to receive the callback. To pass data from the source view controller, ViewController, override the prepareForSegue method, as shown in Listing 3-5.

Listing 3-5. Presenting View Controller Override prepareForSegue

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
  var identifier = segue.identifier
  var destVc = (segue.destinationViewController as PresentedViewController)
  destVc.data = "some data from presenting vc (identifier)"
}

Container View Controller

In iOS, screen navigations are primarily implemented by storyboard segues and the Container View Controller classes from the iOS SDK that facilitate the screen navigations. You may create subclasses from these system Container View Controllers, but you normally just use them as is in most of the cases.

 Note  Generally speaking, the navigation patterns in this book should appear more familiar to mobile web developers with experience in responsive design or using a mobile framework. For traditional web developers, it is a small investment to get familiar with mobile web development. In addition to HTML5/CSS3, the jQueryMobile and Sencha Touch frameworks are both good choices to leap into this space.

Navigation Stack

The navigation stack is widely used to manage screen transitions, particularly for displaying an information hierarchy, such as a master drill-down list. To show the next screen, push the next view controller into the navigation stack. To go back to the previous screen, pop out the previous view controller from the navigation stack.

You will be guided to create a simple iOS app yourself so you know how to create it and can visualize how it works (see Figure 3-26).

  • It has three content views.
  • The screen transition is animated that shows the navigation directions.

9781484209325_Fig03-26.jpg

Figure 3-26. Preview of the iOS NavigationStack app with three screens

In iOS, you draw appropriate storyboard segues with the UINavigationController Container View Controller to accomplish this navigation stack pattern. Let’s create a new Xcode project to demonstrate the iOS way.

  1. Create a new Xcode project using the iOS Single View Application template (see Chapter 2). Name it NavigationStack.
  2. Select Main.storyboard to open the storyboard in the Editor area. It has one scene already.
  3. Add two more Content View (scenes): drag a View Controller object from the Object Library onto the storyboard twice. Figure 3-27 depicts the storyboard containing three empty scenes.

    9781484209325_Fig03-27.jpg

    Figure 3-27. Three storyboard scenes in the NavigationStack project

  4. Use the preview in Figure 3-26 to guide you as you update the first storyboard scene.
    1. Add a Label from the Object Library. Change its font size to 30 and text to Screen One, and center its alignment with the Attributes Inspector in the Utility area.
    2. Add Auto Layout constraints to center the label, as shown in Figure 3-28.
    3. Add the Next button with the right and bottom space constraints to anchor the position, as shown in Figure 3-29.

    9781484209325_Fig03-28.jpg

    Figure 3-28. Screen One label with center constraints

    9781484209325_Fig03-29.jpg

    Figure 3-29. Next button with alignment constraints

  5. Repeat step 4 to add a Screen Two label and the Next button to the Screen Two scene.
  6. Repeat step 4 to add a Screen Three label to the Screen Three scene. Figure 3-30 shows the UI widgets added to storyboard.

    9781484209325_Fig03-30.jpg

    Figure 3-30. Three scenes with widgets in NavigationStack project

  7. Create an action segue from the Next button in Screen One to the Screen Two scene, and create another action segue from the Next button in Screen Two to the Screen Three scene.

Figure 3-31 shows the results of the storyboard. Run the app to see what is working and what is not working yet. Nothing is new yet; these are the same steps that create storyboard scenes, UI widgets, Auto Layout constraints, and segues that connect them together.

9781484209325_Fig03-31.jpg

Figure 3-31. NavigationStack storyboard

UINavigationController

The only missing piece in this iOS project is a way to show the appropriate child view controller in the typical push and pop fashions. In iOS, UINavigationController manages the push and pop screen navigation stack behaviors. It also provides a navigation bar that has a default Back button, a title in the center, and an optional right button (see Figure 3-32).

9781484209325_Fig03-32.jpg

Figure 3-32. The UINavigationController navigation bar

All you need to do is to set up a UINavigationController that associates its root view controller with the first scene.

  • Add a Navigation Controller from the Object Library.
  • Connect the root view controller segue (in the Connections Inspector) to the first view controller.

You can accomplish both in one simple storyboard operation: embed the Screen One view controller in a Navigation Controller, as shown in the following steps:

  1. Select the Screen One view controller from the storyboard.
  2. In the Xcode menu bar, select Editor image Embed In image Navigation Controller, as shown in Figure 3-33.

    9781484209325_Fig03-33.jpg

    Figure 3-33. Creating a Navigation Controller

  3. This creates a Navigation Controller and connects the root view controller segue to the Screen One view controller (see Figure 3-34).
    1. The Navigation Controller has a NavigationBar.
    2. The root view controller automatically gets a NavigationItem where you can add a center title and a rightBarButtonItem.

      9781484209325_Fig03-34.jpg

      Figure 3-34. Navigation Controller scene and root view controller connection

  4. Select the Navigation Item object in Screen One to add title text (that is, type Navigation Stack), as shown in Figure 3-35.
    1. The title on the navigation bar also affects the Back button title. iOS automatically updates the button title attribute to reflect where the Back button is going. The button text defaults to Back if the title is not assigned in the view.

    9781484209325_Fig03-35.jpg

    Figure 3-35. Title on the Navigation Item

  5. Optional: The title and the rightBarButtonItem need to be installed on Navigation Item, as shown in previous step. If you want to set the title attribute or rightBarButtonItem to Screen Two or Three, you need to add a Navigation Item to the Screen Two or Three view controller first.

 Note  The title can be derived from the view controller title when the Navigation Item is not being configured.

Build and run the app to see the live app in action. You should see that your app looks like Figure 3-26 (shown previously).

Master List with Details Drill-Down

Many apps need to display a list of items that users can select to view more detailed information. They present a master list of items first, and the user selects one item to drill into.

This is probably one of the most common mobile navigation patterns. Most of the popular mobile frameworks including iOS, Android, jQuery mobile, and Sencha Touch mobile JavaScript libraries take care of the memory management for you using recycle views without rendering off-screen list items.

Android and iOS provide guidelines and offer system APIs to promote consistency by making the implementation easy for developers. Both Xcode and the Android IDE supply project templates for creating apps with this UX pattern.

You will create a simple iOS app (see Figure 3-36) to visualize how this works and how to create it.

9781484209325_Fig03-36.jpg

Figure 3-36. Preview of the MasterDetail app screens in iOS

UITableViewController

As you can see in Figure 3-36, this app is purposely simple with two screens only. You need two storyboard scenes: one for the master list and the other for the detailed Content View. To make a fresh start, create a new iOS project.

  1. Launch Xcode, create a new application using the usual Single View Application template, and name it MasterDetail.
    1. You get a ViewController class that already pairs with a scene in Main.storyboard. Use this pair for the detail view screen.
  2. Create the master list storyboard scene and draw a segue to connect the two scenes.
    1. Drag UITableViewController from the Object Library and drop it in the storyboard editor.
    2. Create a Manual Segue from the UITableViewController to the detailed view controller with the Show transition type. Always give the segue an identifier, such as detail (see the earlier Figure 3-25).
  3. Select the Table View Cell in the UITableViewController scene and open the Attributes Inspector to configure the Table View Cell (see the pointers in Figure 3-37).
    1. Style: Select Basic (or others to see what they are in the editor).
    2. Identifier: Enter mycell. Always give the table view cell an identifier. You need it to create reusable cells.
    3. Accessory: Select Detail.
    4. Optionally, you can add an image that shows the icon on the left.

    9781484209325_Fig03-37.jpg

    Figure 3-37. TableViewCell attributes

  4. Embed the UITableViewController in a Navigation Controller (see the earlier Figure 3-33). You normally use a navigation stack pattern for the screen transitions.
  5. Check the Is the Initial View Controller in the Navigation Controller Attributes Inspector (see Figure 3-38) to make the Navigation Controller the starting view controller of this app.

    9781484209325_Fig03-38.jpg

    Figure 3-38. MasterDetail storyboard

  6. Create a MasterTableViewController Swift class in the ViewController.swift file, as shown in Listing 3-6.
    1. Subclass MasterTableViewController from UITableViewController.
    2. Pair this MasterTableViewController class with the Table View Controller scene in the Identity Inspector (see the earlier Figure 3-17).

Listing 3-6. MasterListTableViewController Class

class MasterTableViewController : UITableViewController {
  // TODO
}

 Note  In Swift, the class doesn’t need to be in its own file. I chose to put it in the existing ViewController.swift file for no particular reason.

UITableViewDataSource

To populate the items in the Table View, UITableView, you implement a data source. Specifically, you provide the TableViewCell with data by overriding the methods defined in the UITableViewDataSource protocol (see Listing 3-7). Do the following in the MasterDetail project:

  1. Implement tableView(tableView,numberOfRowsInSection) to return the number of items in the tableView.
  2. Implement tableView(tableView, cellForRowAtIndexPath) to return the tableViewCell instance.

Listing 3-7. Implement UITableViewDataSource Protocol

class MasterTableViewController : UITableViewController {
  var items = ["item 1", "item 2", "item 3"]
  
  override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.items.count
  }
  
  override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCellWithIdentifier("mycell") as UITableViewCell
    cell.textLabel!.text = self.items[indexPath.row]
    return cell
  }
}

 Note  Use dequeueReusableCellWithIdentifier(...) to implement recycled views. This is a common pattern for saving memory with a large number of items, and iOS makes it easy by offering this method. Make sure you assign a cell identifier to the table view cell in the storyboard.

UITableViewDelegate

To handle table view item selected events, override the UITableViewDelegate.tableView(tableView, didSelectRowAtIndexPath) method (see Listing 3-8).

  1. Implement UITableViewDelegate.didSelectRowAtIndexPath(...) to perform segues.
  2. Implement a prepareForSegue(...) callback to pass data to the detail view controller (see Listing 3-5 for details).

    Listing 3-8. Implementing UITableViewDelegate

    class MasterTableViewController : UITableViewController {
      ...
      override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        self.performSegueWithIdentifier("detail", sender:     indexPath.row)
      }
      
      override func prepareForSegue(segue: UIStoryboardSegue,   sender: AnyObject) {
        var destVc = segue.destinationViewController as     UIViewController
        destVc.navigationItem.title = self.items[sender as Int]
      }
  3. After adding or deleting items, you need to explicitly refresh the table view.
    1. Drag a Bar Button Item object to the navigation bar and draw an IBAction to create the doAdd() method (Figure 3-39).
    2. To add an item when the Add button is selected, implement the doAdd() method, as shown in Listing 3-9 Make sure you call TableView.reloadData() in the main thread to refresh the table.

Listing 3-9. Refresh Table View

class MasterTableViewController : UITableViewController {
  var items = ["item 1", "item 2", "item 3"]
  
  @IBAction func doAdd(sender: AnyObject) {
    self.items.append("item (self.items.count + 1)")
    self.tableView.reloadData()
  }
  ...

9781484209325_Fig03-39.jpg

Figure 3-39. Navigation bar right button

Build and run (flower.jpg+R) the app to see the MasterDetail iOS app in action. You should see that your app looks like the preview shown previously in Figure 3-36.

UITableView

Previously, you used UITableViewController, which is a regular UIViewController with a prewired tableView object in it. The prewired tableView also prewires the dataSource and delegate to the host UITableViewController, which simplifies some coding for you (actually not that much, in my opinion). The prewired tableView takes up the whole screen, which may not be what you want. More often, I choose to do the following instead of using UITableViewController.

  1. In the storyboard, create a regular ViewController with TableView.
    1. Add a regular View Controller scene.
    2. Add a table view to the scene. This gives you more flexibility (that is, to draw the table view at any location and size).
    3. Connect the table view to an IBOutlet (see the earlier Figure 3-20 for details).
    4. Select the table view and connect delegate and dataSource outlets to the ViewController in the Connection Inspector.
  2. Create a Swift class to pair with the Content View.
    1. Subclass from the regular UIViewController class.
    2. Implement the UITableViewDataSource and UITableViewDelegate protocols.

The previous MasterTableViewController is essentially equivalent to the code in Listing 3-10, and you still implement the same methods declared in UITableViewDataSource and UITableViewDelegate.

Listing 3-10. Explicitly Implement Table View Protocols

class MasterTableViewController2 : UIViewController, UITableViewDataSource, UITableViewDelegate {
  @IBOutlet weak var tableView: UITableView!
  ...

UITableViewCell

In iOS, the line items in the UITableView are called cells. You get some free types of UITableViewCell that you can select from the iOS SDK. Previously in the MasterDetail project, you selected the Basic style, which gives you one textLabel in the Table View cell (see Figure 3-37). Right Details, Left Details, or Subtitle styles all give you a second label called detailedTextLabel. You can also set a left image icon and other TableViewCell attributes, as shown in Figure 3-37.

Still, it is the data source’s responsibility to render the cell with data: You programmatically bind each UITableViewCell with data by implementing the tableView(cellForRowAtIndexPath) method. Listing 3-11 shows a typical example.

Listing 3-11. TableViewCell Properties

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  var cell = ...
  cell.textLabel!.text = self.items[indexPath.row]
  cell.detailTextLabel!.text = "some detail label"
  cell.imageView!.image = UIImage(named: "pointer.png")
  cell.accessoryType = UITableViewCellAccessoryType.DetailButton
  return cell
}

You can also choose the Custom style and draw the cell freely using the storyboard; you will do so in the next section.

UICollectionView

The MasterDetail project you created fits comfortably on an iPhone, but when it is running on the iPad, it feels like the space is not being utilized efficiently. On iOS platforms, UICollectionView comes to the rescue: it allows multiple columns to organize the master list items, rather than a simple one-dimensional list.

It would be a shame if you didn’t try this variant right now because it is a really useful widget that takes very little extra effort. The key is the UICollectionView class.

 Note  You can also use UICollectionViewController, which contains a UICollectionView that occupies the whole scene by default. Same choice: UITableViewController versus UITableView, which was discussed earlier (see the “UITableView” section).

Create a new Xcode project using UICollectionView to demonstrate the usage.

  1. Launch Xcode and create a new application using the usual Single View Application template. Name it MasterGridDetail.
    1. You get a scene in the Main.storyboard file and a ViewController class pair.
    2. Rename the class to MasterViewController in the ViewController.swift file and as the custom class name of the view controller scene (see Figure 3-17).
    3. Drag a Collection View from the Object Library and drop it onto the MasterViewController scene. Let it take up the whole space and pin zero spacing to the Superview in all four directions from the Xcode menu bar by selecting Editor image Pin.
    4. Embed the MasterViewController controller in the Navigation Controller (see Figure 3-33).
  2. Select the Collection View in the storyboard to create connections in the Connections Inspector, as shown in Figure 3-40.
    1. Connect the dataSource and delegate outlets to the MasterViewController.
    2. Open the Assistant Editor and connect the New Referencing Outlet to the MasterViewController property. Enter mCollectionView for its name.

    9781484209325_Fig03-40.jpg

    Figure 3-40. Collection View dataSource, delegate, and IBOutlet connections

  3. Draw your own custom collection view cell in the storyboard.
    1. In the Attributes Inspector, assign the Collection Reusable View Cell Identifier a value (mycell). There are some attributes you can change safely in the Attributes Inspector, such as the white background color.
    2. To change the size of the cell, select the parent collection view and change the cell size to 150 × 150 in the Size Inspector. Figure 3-41 shows other measurements that you can set.

      9781484209325_Fig03-41.jpg

      Figure 3-41. Size Inspector

    3. Add a label to the cell, make the font size bigger (that is, 30), use center alignment, and add Auto Layout constraints, as shown in Figure 3-42.

      9781484209325_Fig03-42.jpg

      Figure 3-42. Drawing the collection view cell

  4. Create a custom Swift class for the collection view cell. Listing 3-12 shows the SimpleCollectionViewCell class.
    1. Create a Swift class, SimpleCollectionViewCell, subclassed from UICollectionViewCell.
    2. Select the collection view cell in the storyboard and assign the SimpleCollectionViewCell class in the Identity Inspector.
    3. Open the Assistant Editor with the SimpleCollectionViewCell class. In the Connections Inspector, connect the referencing outlet to create an IBOutlet, and name it textLabel.

    Listing 3-12. SimpleCollectionViewCell Class

    class SimpleCollectionViewCell : UICollectionViewCell {
      @IBOutlet weak var textLabel: UILabel!
    }
  5. Create the detail view controller scene in the storyboard, as shown Figure 3-43.
    1. Add a regular view controller from the Object Library.
    2. Create a manual segue from the Master View Controller scene to the detailed view controller with a Show transition type (see Figure 3-25). Always enter a storyboard segue Identifier (detail).

    9781484209325_Fig03-43.jpg

    Figure 3-43. Creating the detail view controller in MasterDetail storyboard

  6. MasterViewController must implement the UICollectionViewDataSource and UICollectionViewDelegate protocols (see Listing 3-13).
    1. Implement numberOfSectionsInCollectionView(collectionView) to return the section number; it defaults to 1 if not implemented.
    2. Implement collectionView(collectionView, numberOfItemsInSection) to return the number of items in each section.
    3. Implement collectionView(collectionView, cellForItemAtIndexPath) to return the collection view cell instance.
    4. Implement collectionView(collectionView, didSelectItemAtIndexPath) to respond to the cell selection.

Listing 3-13. UICollectionViewDataSource and UICollectionViewDelegate Protocols

class MasterViewController : UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
  ...
  // implement UICollectionViewDataSource
  var items = ["item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7"]
  func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.items.count
  }
  
  // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
  func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    var cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as SimpleCollectionViewCell
    cell.textLabel.text = self.items[indexPath.row]
    cell.backgroundColor = (indexPath.row % 2 == 0) ? UIColor.whiteColor() : UIColor.lightGrayColor()
    return cell
  }

  func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1
  }

  // implement UICollectionViewDelegate
  func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    self.performSegueWithIdentifier("detail", sender: self)
  }
}

Both TableView and CollectionView are versatile. You should look into the data source and delegate protocols to see the rich options offered to developers. Build and run the MasterGridDetail iOS app to see your code live in action, as shown in Figure 3-44.

9781484209325_Fig03-44.jpg

Figure 3-44. Collection view

Navigation Tabs

WEB ANALOGY

Navigation tabs are easily implemented in a web app by using a UI widget library like jQueryUI or Bootstrap.

Navigation tabs are another popular UX design pattern. Apple’s iOS Human Interface Guidelines suggest using a tab bar to give users access to different perspectives on the same set of data or on different subtasks related to the overall function of your app. Each navigation tab is associated with a view controller. When the user selects a specific tab, the associated view controller presents its own Content View.

Not only does this pattern exist in iOS, but it also widely used in many mobile platforms. You may simply call it tabs in desktop or web apps.

You will create a simple tabbed iOS app (see Figure 3-45) to visualize how this works and how to create it.

9781484209325_Fig03-45.jpg

Figure 3-45. Preview of the iOS TabbedApp

The key in iOS is the Container View Controller, which is the UITabBarController class. You can use it as is most of the time. If you want to keep some application states in the Container View Controller, simply subclass from it.

Implementing Navigation Tabs

The following instructions walk you through the steps you normally take to implement navigation tabs:

  1. Launch Xcode to create a new app using the Single View Application template and name it TabbedApp.
    1. You get an empty scene in the Main.storyboard and a ViewController class.
    2. Rename the class to FirstViewController in both the ViewController.swift file and the class name in the Identity Inspector for the storyboard scene.
    3. Draw the Content View in the storyboard scene using the Android app as your wireframe.
  2. You need the second Content View and view controller pair.
    1. You can copy, paste, and modify from the FirstViewController class. Listing 3-14 shows the SecondViewController class.

      Listing 3-14. SecondViewController Class

      class SecondViewController: UIViewController {
        ...
      }
    2. You can copy, paste, and modify the storyboard scene in the storyboard editor, too. Don’t forget to update the class name in the Identify Inspector. Figure 3-46 shows that the storyboard has the Screen One and Screen Two scenes.

      9781484209325_Fig03-46.jpg

      Figure 3-46. First and second scenes in TabbedApp

  3. Add a Container View Controller, UITabBarController, which will be responsible for managing the two Content View Controllers.
    • c. Embed both storyboard scenes in a TabBarController: multiselect both storyboard scenes (press and hold the flower.jpg key for multiselect) and select the command from Editor image Embed In image Tab Bar Controller in the Xcode menu bar (see Figure 3-47).

    9781484209325_Fig03-47.jpg

    Figure 3-47. Embed Content Views in Tab Bar Controller (the result on the right)

The app is not complete yet, but you actually can run it now to see the app live. It should look like the preview shown in Figure 3-45.

UITabBarController

As you can see from the previous work (see Figure 3-45), a bottom tab bar comes with UITabBarController. Each tab bar item is associated with a Content View Controller. By selecting a tab bar item, the UITabBarController automatically presents the selected Content View Controller.

Add/Remove a Tab Bar Item

UITabBarController maintains an array of references to its child view controllers. In its Connections Inspector, you can draw a view controller’s outlet in the Triggered Segues section to add the child view controllers to the UITabBarController.viewControllers array property. You can write code to add or remove the child view controller in the runtime, but doing so in a storyboard is easier and more robust for creating static tabs.

Update the Look and Feel of the Tab Bar Items

You should assign a title and an image to the tab bar items. When a UIViewController is added to a tab bar controller, the UIViewController.tabBarItem property represents the tab bar item in the tab bar. You assign appropriate values in the tab bar item’s Attributes Inspector. In the runtime, you use code to update the text and image by setting appropriate tabBarItem properties. Figure 3-48 shows two tab bar items with the same label and without an icon image. Do the following to give your TabbedApp a better look:

  1. System provides a set of common tab bar items (System Item in the Attributes Inspector). Use them when possible for consistent platform convention. Select the Featured system item for Screen One (see Figure 3-48).

    9781484209325_Fig03-48.jpg

    Figure 3-48. Adding the Featured system item to Screen One

  2. For Screen Two, select Custom to supply your own tab bar item title and image icon (see Figure 3-49).
    1. Enter Two for the title.
    2. Create an image icon in images.xcassets (in other words, tab1) and drop a transparent PNG with size about 25 × 25 (maximum 48 × 32). The icon color is not required because only the alpha channel is to be rendered.

       Note  iOS is picky about the right image specs. See the online reference for image specs: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html.

    3. Enter the image name (for example, tab1).
    4. You may enter a badge (for example, New), which will appear in the upper-right corner of the icon. Frequently, it is set programmatically in the runtime using the UITabbatItem.badgeValue property.

9781484209325_Fig03-49.jpg

Figure 3-49. Custom tab item with badgeValue

Handle Runtime Behavior

To respond to the runtime behavior programatically, just like most of the UIKit widgets, you implement a delegate protocol: UITabBarDelegate. Continue with the TabbedApp to learn the common tasks for handling UITabBarDelegate runtime events.

  1. Create a custom tab bar controller to handle runtime behaviors, as shown in Listing 3-15.
    1. Create a SimpleTabBarController class subclassing from UITabBarController.
    2. Declare SimpleTabBarController to implement the UITabBarControllerDelegate protocol.
    3. In the storyboard, select the tab bar controller and assign the SimpleTabBarController class in the Identity Inspector.

    Listing 3-15. SimpleTabBarController Class

    class SimpleTabBarController : UITabBarController, UITabBarControllerDelegate {
      override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view ...
        self.delegate = self
      }

      func tabBarController(tabBarController: UITabBarController!, shouldSelectViewController viewController: UIViewController!) -> Bool {
        // you may do something and return true
        // Or, return false to not to select viewController
        return false;
      }

      func tabBarController(tabBarController: UITabBarController!, didSelectViewController viewController: UIViewController!) {
        // you may do something
      }
    }
  2. Each child Content View Controller can access the UIViewController.tabBarController and UIViewController.tabBarItem properties. Listing 3-16 shows how to change the second tab’s badgeValue from the FirstViewController.

Listing 3-16. Change badgeValue of Other tabBarItem

(self.tabBarController!.viewControllers![1] as UIViewController).tabBarItem.badgeValue = "Zzz"

By the way, the tab bar is located on the bottom screen in iOS instead of at the top in Android or many web sites. In general, platform-specific UX guidelines should never be overlooked. Keep the navigation bar on the bottom in iOS, which is where most iOS users expect it.

Swipe Views

The carousel is a popular UX pattern commonly used in many platforms, including desktop and web apps. On mobile platforms, you use this pattern with swipe gestures to display content page by page. It allows the user to move from item to item efficiently. With animated transitions, it offers a more enjoyable viewing experience. Both iOS and many JavaScript libraries provide framework classes or APIs, as well as a project creation template to promote this navigation UX pattern.

You will be guided to create a simple SwipeViews iOS app (see Figure 3-50) to visualize how this works and how to create it.

9781484209325_Fig03-50.jpg

Figure 3-50. Preview of the iOS SwipeViews app

As usual for a top-down approach, you want to construct the storyboard scenes to start.

  1. Launch Xcode to create a new project using the usual Single View Application template and name it SwipeViews.
  2. Rename the existing class ViewController to ParentViewController in both the ViewController.swift file and the custom class name in the Identity Inspector of the view controller’s storyboard scene.
  3. Drag and drop a UIView onto the storyboard scene from the Object Library.
    1. Open the storyboard Assistant Editor, create an IBOutlet in the Connections Inspector by connecting the referencing outlet to the paired view controller, and name it mPageView.
    2. Add Auto Layout constraints to pin the edges to the nearest neighbors and set the background to be light gray.
  4. You need another Content View–view controller pair for the page content.
    1. Create a new PageContentViewController class (see Listing 3-17).

      Listing 3-17. Two Classes in ViewController.swift File

      import UIKit
      class ParentViewController : UIViewController {
        @IBOutlet weak var mPageView: UIView!
        
        override func viewDidLoad() {
          super.viewDidLoad()
        }

        override func didReceiveMemoryWarning() {
          super.didReceiveMemoryWarning()
          // Dispose of any resources that can be recreated.
        }
      }

      class PageContentViewController: UIViewController {
        @IBOutlet weak var textLabel : UILabel!
      }
    2. Draw the second storyboard scene (see the screen on the right in Figure 3-51). Make sure you give the Page Content View Controller a storyboard ID (such as PageContentViewController). You need the ID to programmatically load a view controller from the storyboard.

9781484209325_Fig03-51.jpg

Figure 3-51. Main.storyboard in SwipeViews project

Nothing is new yet; you just drew two storyboard scenes with the view controllers to start with. You may build and run the app to make sure it contains no errors. Unlike previous navigation stack or tab patterns to which you can draw segues for view controller transitions, you need to write code to complete the app, which you will do next.

UIPageViewController

The key to implementing the swipe view UX pattern in iOS is the UIPageViewController class, which uses the same data source pattern: you implement a data source that is responsible for providing Content Views populated with data. Continue with the following steps in the SwipeViews Xcode project:

  1. Modify the PageContentViewController class so it can receive data and present the three simple screens (see Listing 3-18).

    Listing 3-18. Add the data and pageNo Properties

    class PageContentViewController: UIViewController {
      
      @IBOutlet weak var textLabel : UILabel!

      var data = ""
      var pageNo = 0
      
      override func viewDidLoad() {
        self.textLabel.text = data
      }
    }
  2. UIPageViewController works with the dataSource and delegate protocols. The parent view controller, ParentViewController, is a legitimate candidate for implementing them (see Listing 3-19).
    1. UIPageViewControllerDataSource is responsible for supplying the Content View Controller before and after the current content one.
    2. UIPageViewControllerDataSource also defines the optional page count and page selection indicator.
    3. UIPageViewControllerDelegate protocol defines the optional page view controller callback methods.

    Listing 3-19. ParentViewController Implements dataSource and delegate Protocol

    class ParentViewController : UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

      ...
      // implement data source
      let items = ["Page: 1", "Page: 2", "Page: 3"]
      func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {

        var pageNo = (viewController as PageContentViewController).pageNo
        if pageNo > 0 {
          var vc = self.storyboard!.instantiateViewControllerWithIdentifier("PageContentViewController") as PageContentViewController
          vc.data = self.items[pageNo-1]
          vc.pageNo = pageNo - 1
          return vc
        }
        
        return nil
      }
      
      func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {

        var pageNo = (viewController as PageContentViewController).pageNo
        if pageNo < self.items.count - 1 {
          var vc = self.storyboard!.instantiateViewControllerWithIdentifier("PageContentViewController") as PageContentViewController
          vc.data = self.items[pageNo+1]
          vc.pageNo = pageNo + 1
          return vc
        }
        
        return nil
      }
      
      func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
        return self.items.count
      }

      func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
        return (pageViewController.viewControllers[0] as PageContentViewController).pageNo
      }
      ...
  3. In the ParentViewController viewDidLoad() method, the following conventional code (see Listing 3-20) sets up the UIPageViewController:
    1. Initialize the page view controller.
    2. Set up the dataSource and delegate.
    3. Set up the first page view controller.
    4. Establish the parent-child view controller hierarchy.

Listing 3-20. Setting Up UIPageViewController in viewDidLoad

class ParentViewController : UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

  @IBOutlet weak var mPageView: UIView!
  var mPageViewController: UIPageViewController!

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
        
    // a. initialize page view controller, view and gestures
    self.mPageViewController = UIPageViewController(transitionStyle: UIPageViewControllerTransitionStyle.Scroll, navigationOrientation: UIPageViewControllerNavigationOrientation.Horizontal, options: nil)
    self.mPageViewController.view.frame = self.mPageView.bounds
    self.mPageView.gestureRecognizers = self.mPageViewController.gestureRecognizers
    
    // b. set data source and delegate
    self.mPageViewController!.delegate = self
    self.mPageViewController!.dataSource = self

    // c. set the first page
    var vc = self.storyboard!.instantiateViewControllerWithIdentifier("PageContentViewController") as PageContentViewController
    vc.data = self.items[0]
    vc.pageNo = 0
    self.mPageViewController.setViewControllers([vc], direction: .Forward, animated: false, completion: nil)
    
    // d. establish parent-child view and view controller hierachy
    self.mPageView.addSubview(self.mPageViewController.view)
    self.addChildViewController(self.mPageViewController)
    self.mPageViewController.didMoveToParentViewController(self)
  }
  ...

This is all about the UIPageViewController class and the swipe view navigation pattern in iOS. Build and run the iOS SwipeViews app to see your code in action. It should look like the preview shown in Figure 3-50.

Dialogs

WEB ANALOGY

Web application dialogs typically use the built-in window.alert() or window.confirm() method, or they use a modal dialog via an add-on UI library.

Generally, you use the dialogs UX pattern to give users quick feedback or to request a simple confirmation of choices. Dialogs normally sit on top of the current screen while that screen remains partially visible or dimmed. This creates a visual effect that is meant to get more user attention without losing the current context.

Create an Xcode project to demonstrate the uses of dialogs and common programming tasks.

  1. Launch Xcode to create a new project using the Single View Application template and name it Dialogs.
  2. Draw two Button widgets with titles set to Alert and Popup in the storyboard scene, as shown in Figure 3-52.

    9781484209325_Fig03-52.jpg

    Figure 3-52. Dialogs storyboard

  3. Open the storyboard Assistant Editor and connect the Send Event image Touch Down outlet in the Connections Inspector to create IBAction methods in the paired ViewController class for both buttons (see doAlert(...) and doPopup(...) in Listing 3-21).

Listing 3-21. Create IBAction Methods for the Alert and Pop-up Buttons

import UIKit
class ViewController: UIViewController, UIAlertViewDelegate {
  ...
  @IBAction func doAlert(sender: AnyObject) {
    // TODO
  }
  @IBAction func doPopup(sender: AnyObject) {
    // TODO
  }
}

Nothing should be new to you here, but this Dialogs Xcode project should give you a refresher on dialog topics.

UIAlertController

WEB ANALOGY

The window.alert() method halts code execution and offers only one action for the user: to close the alert dialog and continue.

Figure 3-53 shows the standard Android and iOS alert dialogs side by side. On iOS, you use UIAlertController that replaces UIAlertView since iOS 8.

9781484209325_Fig03-53.jpg

Figure 3-53. Android (left) and iOS (right) alert dialogs

Contiune with the Xcode Dialogs project and add the following code to learn the UIAlertController code:

  1. To show the iOS dialog as depicted in Figure 3-53, add the following code in the ViewController.doAlert(...) method (see Listing 3-22):
    1. Create a UIAlertController instance.
    2. Add UIAlertAction for dialog buttons.
    3. To prompt user input, add TextField to UIAlertController.
    4. Use the regular UIViewController API to present it as a view controller.

Listing 3-22. Present  UIAlertController

@IBAction func doAlert(sender: AnyObject) {
    
  var alert = UIAlertController(title: "My title", message: "My message", preferredStyle: UIAlertControllerStyle.Alert)
    
  // add action buttons
  var actionCancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel,
    handler: {action in
        // do nothing
      })
    
    var actionOk = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default,
      handler: {action in
        println((alert.textFields![0] as UITextField).text)
      })
    
    alert.addAction(actionCancel)
    alert.addAction(actionOk)
    
    // add text fields
    alert.addTextFieldWithConfigurationHandler({textField in
      // config the UITextField
      textField.backgroundColor = UIColor.yellowColor()
      textField.placeholder = "enter text, i.e., Do Ra Me"
      })
    
    // UIViewController API to presend viewController
    self.presentViewController(alert, animated: true, completion: nil)
  }

Everything inside UIAlertController is created programatically. Figure 3-54 shows the UIAlertController code in action.

9781484209325_Fig03-54.jpg

Figure 3-54. Android iOS UIAlertController

UIPopoverController

On iOS devices with large screens such as the iPad, you can use PopoverController to present a regular viewController as a pop-over. The pop-over can be anchored at a given position to associate the presented dialog with the presenting context. On devices with compact size classes such as the iPhone, the pop-over controller automatically falls back to the regular full-screen view controller. You can also take advantage of the storyboard editor to draw the Content Views and segue instead of writing code.

The following steps demonstrate how to use PopoverController in the Dialogs project:

  1. Add a new storyboard scene for the pop-over Content View (see Figure 3-55).

    9781484209325_Fig03-55.jpg

    Figure 3-55. Dialogs storyboard completion

    1. Create a GreenViewController class, as shown in Listing 3-23, and draw a view controller scene in the storyboard to pair with it by specifying the class name in the Identity Inspector.

      Listing 3-23. GreenViewController Class

      class GreenViewController : UIViewController {
        @IBAction func doDone(sender: AnyObject) {
          // do something and dismiss
          self.dismissViewControllerAnimated(true, completion:nil)
        }
      }
    2. Embed the view controller in a Navigation Controller (Editor image Embed in from the Xcode menu bar) to take advantage of the navigation bar title or any Navigation Controller features.
    3. In the Navigation Controller Attributes Inspector, change the Simulated Metrics Size value to Freeform; then change the Simulated Size value to 250 x 300 in the Size Inspector.
    4. In the Navigation Controller Identity Inspector, specify the storyboard ID as nav. You always need the ID if you want to instantiate a view controller instance directly from a storyboard (see Listing 3-24).
    5. You normally draw meaningful contents in a Content View. For simplicity, use the Attributes Inspector to change the GreenViewController view’s Background attribute to Green.
    6. Draw a BarButtonItem on the right side of navigation bar in the GreenViewController and connect the Send Actions outlet to the IBAction method in GreenViewController, as shown in Listing 3-23. Figure 3-55 depicts the completed storyboard.
  2. To present the GreenViewController, draw a manual segue from the presenting ViewController to the parent Navigation Controller of GreenViewController. Specify the segue attribute values as shown in the Attributes Inspector in Figure 3-56.
    1. Identifier: Set this to mypopover.
    2. Segue type: Set this to Present As Popover.
    3. Direction: Set this to Up (you may play with other values).
    4. Anchor outlet: Drag the outlet to the Popup button in the presenting view controller.

    9781484209325_Fig03-56.jpg

    Figure 3-56. Storyboard segue attributes in the Attributes Inspector

  3. To perform the mypopover manual segue, update the ViewController.doPopup(...) IBAction method as shown in Listing 3-24. Note that the commented code programmatically presents a UIPopoverController without using the storyboard segue.

    Listing 3-24. Perform the mypopover Manual Segue

    // In ViewController class
    @IBAction func doPopup(sender: AnyObject) {
      self.performSegueWithIdentifier("mypopover", sender: nil)
        
    // var nav = self.storyboard!.instantiateViewControllerWithIdentifier("nav") as UIViewController
    // var popover = UIPopoverController(contentViewController: nav)
    // popover.delegate = self;
    // popover.popoverContentSize = nav.view.bounds.size
    // popover.presentPopoverFromRect(self.mPopupButton.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Up, animated: true)
    }
  4. You can dismiss the pop-over by tapping anywhere inside the presenting view controller but outside the GreenViewController Content View. To dismiss the pop-over programmatically, implement GreenViewController.doDone(...) to call the dismissViewControllerAnimated(...), as shown in Listing 3-23.

You can build and run the Xcode Dialogs project to see the UIPopoverController code in action. UIPopoverController is rendered as a pop-up dialog on an iPad, while the same code renders a full-screen modal screen in compact-sized classes such as the iPhone, as shown in Figure 3-57.

9781484209325_Fig03-57.jpg

Figure 3-57. The results of the UIPopoverController code on the iPad vs. the iPhone

Summary

To start developing an iOS app, first create storyboard scenes using the wireframe or mock-up to break your app naturally into a structured MVC project in a top-down fashion. The result is a set of Content View–View Controller pairs.

Next, to implement the screen navigation patterns, you draw storyboard segues to connect storyboard scenes. You also choose the appropriate Container View Controller (such as UINavigationController) from the SDK to facilitate the screen transitions. You will dive into the details of each screen in Chapter 4.

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

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