An iOS 8 Swift Split View Master-Detail Example

From Techotopia
Revision as of 15:58, 23 December 2015 by Neil (Talk | contribs) (Configuring the Detail View Controller)

Jump to: navigation, search
PreviousTable of ContentsNext
Implementing iOS 8 TableView Navigation using Storyboards in Xcode 6 and SwiftImplementing a Swift Page based iOS 8 Application using UIPageViewController


<google>BUY_IOS8</google>


Whilst it is possible to use the UITableView class as both an information display and application navigation tool on iPhone applications, it is extremely inefficient in terms of the use of screen space when used on an iPad or iPhone 6 Plus. In recognition of this fact, Apple introduced the Split View and Popover concepts for use when developing iOS applications specifically for the iPad and carried this functionality to the iPhone 6 Plus device. The purpose of this chapter is to provide an overview of Split Views and Popovers followed by a tutorial that implements these concepts in a simple example iPad application.


Contents


An Overview of Split View and Popovers

When an iPad or iPhone 6 Plus is in landscape mode, the UISplitViewController class divides the screen into two side-by-side panels which implement a master-detail model. Within this model, the left hand panel is the master panel and presents a list of items to the user. The right hand panel is the detail panel and displays information relating to the currently selected item in the master panel.

A prime example of this concept in action can be seen with the iPad Mail app which lists messages in the master panel and displays the content of the currently selected message in the detail panel.

When an iPad or iPhone 6 Plus device is in portrait mode, however, the Split View Controller hides the master panel so that the detail panel is able to utilize the entire screen. In this instance, the master panel is provided in the form of a full screen table view which segues to the detail view when items are selected from the list. When the device is rotated back to landscape orientation, the master and detail panels appear side by side once again. When in this “split view” mode, a button is provided to hide the master panel, leaving the full screen available for use by the detail panel.

When a split view user interface is run on a smaller iPhone screen, it behaves in the same way as when running on an iPad or iPhone 6 Plus in portrait mode.

The UISplitterViewController is essentially a container to which two child view controllers are added to act as the master (also referred to as the root view controller) and detail views.

In the remainder of this chapter we will work through a tutorial that involves the creation of a simple iOS application that demonstrates the use of a split view configuration.

About the Example Split View Project

The goal of this tutorial is to create an application containing a split view user interface. The master panel will contain a table view listing a number of web site addresses. When a web site URL is selected from the list, the detail panel will load the corresponding web site and display it using a UIWebView component.


Creating the Project

Begin by launching Xcode and creating a new application using the Master-Detail Application template. Enter SplitView as the product name, select Universal from the Devices menu, Swift from the language menu and verify that the Use Core Data option is switched off.

By using this template we save a lot of extra coding effort in the implementation of the split view behavior. Much of the code generated for us is standard boilerplate code that does not change from one split view implementation to another. In fact, much of this template can be copied even if you plan to hand code split view behavior in future applications. That said, there are some unusual aspects to the template which will need to be modified during the course of this tutorial.

Reviewing the Project

The Split View template has created a number of project files for us. The most significant of these are the files relating to the Master View Controller and Detail View Controller classes. These classes correspond to the master and detail views and are named MasterViewController and DetailViewController respectively.

Xcode has also created a custom didFinishLaunchingWithOptions method located within the AppDelegate class. Whilst the custom didFinishLaunchingWithOptions method fits perfectly with our requirements, the template actually creates a simple application in which the date and time may be added to the master view and then displayed in the detail view when selected. Since this is not quite the behavior we need for our example it will be necessary to delete some of this template code as our project progresses.

Finally, a Main.storyboard file has been created configured with the following elements:

  • A UISplitViewController
  • A View Controller named DetailViewController for the detail pane
  • A Table View Controller named MasterViewController for the master pane
  • Two navigation controllers

This represents the standard storyboard configuration for a typical split view implementation. The only changes to this storyboard that will be necessary for this example will be to change the user interface of the detail view pane and to modify the title of the master pane. The rest of the storyboard will remain unchanged. To change the title of the master pane, locate it within the storyboard canvas, double click on the title text which currently reads “Master” and enter “Favorite Web Sites” as the new title as outlined in Figure 30-1:


Ios 8 master detail master title.png

Figure 30-1


Configuring Master View Items

The master view controller created for us by Xcode is actually a subclass of UITableView. The next step, therefore, is to configure the table view to display the list of web sites. For this purpose we will need to configure two array objects to store the web site names and corresponding URL addresses. Select the MasterViewController.swift file and modify it as follows to declare these two arrays:

import UIKit

class MasterViewController: UITableViewController {

    var siteNames: [String]?
    var siteAddresses: [String]?

    var detailViewController: DetailViewController? = nil
    var objects = NSMutableArray()
.
.
.

Having declared the arrays, modify the MasterViewController.swift file further to initialize the arrays in the viewDidLoad method. Note that Xcode has already added some code to the viewDidLoad method for the template example so be sure to remove this before adding the new code below:

override func viewDidLoad() {
    super.viewDidLoad()

    siteNames = ["Yahoo", "Google", "Apple", "eBookFrenzy"]
    siteAddresses = ["http://www.yahoo.com", "http://www.google.com", "http://www.apple.com", "http://www.ebookfrenzy.com"]
        
    if let split = splitViewController {
        let controllers = split.viewControllers
        detailViewController = 
		controllers[controllers.count-1].topViewController 
				as? DetailViewController
    }
}

There are a number of methods that must be implemented in order for the items to appear within the table view object. Fortunately, Xcode has already placed template methods for us to use in the MasterViewController.swift file. First, modify the numberOfRowsInSection method to notify the table view of the number of items to be displayed (in this case, a value equal to the number of items in our siteNames array). Since there is only one section in the table also modify the numberOfSectionsInTableView method accordingly. Note that Xcode has, once again, added some code for the template example so be sure to remove this code where necessary:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1
}

- (NSInteger)tableView:(UITableView *)tableView 
numberOfRowsInSection:(NSInteger)section
{
    return siteNames!.count
}

Next, modify the cellForRowAtIndexPath method to return the item to be displayed, using the row number argument as an index into the siteNames array:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", 
	forIndexPath: indexPath) as! UITableViewCell

    cell.textLabel!.text = siteNames![indexPath.row]
    return cell
}

Click on the run button located in the Xcode toolbar to test the current state of the application, using a physical iPad or iPhone 6 Plus device, or a corresponding simulator as the target. Once the application loads, rotate the device into landscape mode (using the Hardware -> Rotate Left menu if running on a simulator):


Ios 8 split view phase 1.png

Figure 30-2


At this point the items in the master panel actually load the detail view onto the master panel when selected and the detail panel still displays the place holder label provided by Xcode when the project was first created. The next step is to change this behavior and add the web view to the detail view controller. <google>BUY_IOS8</google>

Configuring the Detail View Controller

<google>ADSDAQBOX_FLOW</google> When a user selects a web site item from the master panel, the detail panel will load the selected web site into a web view object.

Begin by selecting the Main.storyboard file, locate the DetailViewController scene, click on the placeholder label that reads “Detail view content goes here” and press the Delete key. Drag and drop a UIWebView object from the object library (View -> Utilities -> Show Object Library) onto the view and resize it so that it fills the entire view area. With the Web View instance selected in the storyboard, use the Auto Layout Pin menu to set Spacing to nearest neighbor constraints on all four sides of the view with the Constrain to margins option disabled.

Select the web view object, display the Assistant Editor panel and verify that the editor is displaying the contents of the DetailViewController.swift file. Ctrl-click on the web view again and drag to a position just below the class declaration line in the Assistant Editor. Release the line and in the resulting connection dialog establish an outlet connection named webView.

Connecting Master Selections to the Detail View

The next task is to configure the detail panel to update based on selections made in the master panel. When a user makes an item selection from the table view the prepareForSegue method in the master view controller is triggered. This template method needs to be modified to assign the selected URL to the detailItem property of the DetailViewController class. Select the MasterViewController.swift file and locate the method. Once again, Xcode has added code to the template example so remove the current code and modify the method as follows:

override func prepareForSegue(segue: UIStoryboardSegue, 
				sender: AnyObject?) {

    if segue.identifier == "showDetail" {
        if let indexPath = self.tableView.indexPathForSelectedRow() {
            let urlString = siteAddresses?[indexPath.row]

            let controller = (segue.destinationViewController 
		as UINavigationController).topViewController 
			as! DetailViewController

            controller.detailItem = urlString
            controller.navigationItem.leftBarButtonItem = 
		splitViewController?.displayModeButtonItem()
            controller.navigationItem.leftItemsSupplementBackButton = true
        }
    }
}

The method begins by verifying that this is the segue associated with the transition to the detail view before identifying the URL of the selected web site by using the selected row as an index into the siteAddresses array. A reference to the destination view controller is then obtained and the detailItem property of that instance set to the URL of the web page to be displayed. Finally, a bar button item is added to the detail navigation bar. The button added is the UISplitViewController’s “display mode” button. This button provides the user with the option to expand the detail view to fill the entire display, causing the master panel to be hidden from view.

Modifying the DetailViewController Class

The DetailViewController class created by Xcode is designed to display some text on a Label view. Given that the Label view has now been replaced by a Web View, some changes to the class will inevitably need to be made. Locate the DetailViewController.swift file in the project navigator panel and locate and modify the configureView method so that it now reads as follows:

func configureView() {
    // Update the user interface for the detail item.

    if let detail: AnyObject = detailItem {

        if let myWebview = webView {
            let url = NSURL(string: detailItem as! String)
            let request = NSURLRequest(URL: url!)
            myWebview.scalesPageToFit = true
            myWebview.loadRequest(request)
        }
    }
}

The code checks to make sure that the detailItem variable has a value assigned to it, verifies that the webView view has been created and then performs a number of steps to load the designated URL into the view. Note that in order to make the web page scale to fit the detail view area the scalePagesToFit property of the UIWebView object is set to true.

Testing the Application

All that remains is to test the application so select a suitable iPad or iPhone 6 Plus device or simulator as the target, click on the Xcode run button and wait for the application to load. In portrait mode only the master panel should be visible. Selecting an item from the master list should trigger the segue to the detail panel within which the selected web page should load into the web view instance.

Tapping the “Master” button in the detail panel should return back to the master panel.

Select the Hardware -> Rotate Left menu option to switch the device into landscape mode and note that both the master and detail panels are now visible:


Ios 8 splitview app running.png

Figure 30-3


Tapping the display mode button located in the top left hand corner of the detail panel navigation bar will cause the detail view to expand to fill the entire screen.

Summary

Split views provide iOS applications with a master-detail interface paradigm designed to make better use of the larger screen space of the device. Where ever possible, this approach should be taken in favor of the table view based navigation common in iPhone based applications. In this chapter we have explored split views in general and worked through the implementation of an example application.


<google>BUY_IOS8</google>



PreviousTable of ContentsNext
Implementing iOS 8 TableView Navigation using Storyboards in Xcode 6 and SwiftImplementing a Swift Page based iOS 8 Application using UIPageViewController