Back in July of 2014, I wrote one of this first posts on Split views, I said I would write another post on the subject. I ended up making this a section in chapter six of my Book I ended up making this section 2 of Chapter six of my Book Swift Swift: UI View Controllers. The section came out as three projects, which I will post each as a separate post here. iOS8 makes split views universal, not just iPad. It does different things depending on class sizes. I thought I’d share with you a preview of that book. For more previews and interesting thoughts, sign up for my newsletter. –Steve
In our first part, we moved data using a delegate from detail to master. In this post we move the other direction.
Moving Data from Master to Detail
Our next part is to move data from the master to the detail. We’ll make a new demo for this to choose a color and display its values in different color identification systems. We use colors since it is easy to make a model, instead of a more complicated order model.
New Project from the Storyboard.
Open a new project with Command-Shift-N. Select a Single View template. Name the project SwiftSplitColorDemo, as a Universal app with Swift as the language. Once saved, go to the storyboard, and delete the view controller there. Drag a split view controller out on to the storyboard from the object library. Click on the split view controller and check on the Is Initial View Controller in the attributes inspector. The storyboard looks slightly different from the template:
The detail is a view controller, not a view controller embedded in a navigation controller as the template. Add three labels, and set up the view controller to look like this with white backgrounds on the labels, and a light gray background for the superview:
Add auto layout by , pinning the Hue: label 50 points up, 0 left and 0 right with a height of 50 points. Pin the Red label 10 points up, 0 left and 0 right with a height of 50 points Pin the Hex RGB label 10 points up, 0 left and 0 right with a height of 50 points. Update the frames.
The Colorful Detail View Controller
Press Command-N and make a new Cocoa Touch Class named ColorViewController subclassing UIViewController
with Swift as the language. Add the following code to the controller:
class ColorViewController: UIViewController { @IBOutlet weak var hsbLabel: UILabel! @IBOutlet weak var rgbLabel: UILabel! @IBOutlet weak var hexLabel: UILabel! func displayColor(color:UIColor){ view.backgroundColor = color var red:CGFloat = 0.0,green:CGFloat = 0.0,blue:CGFloat = 0.0 var hue:CGFloat = 0.0, sat:CGFloat = 0.0,bright:CGFloat = 0.0 var alpha:CGFloat = 0.0 let hueFormat = "H:%5.2f S:%5.2f B:%5.2f" let rgbFormat = "R:%0.2f G:%0.2f B:%0.2f" let hexFormat = "Hex: %02X%02X%02X" if color.getHue(&hue,saturation: &sat,brightness: &bright,alpha: &alpha){ hsbLabel.text = String(format:hueFormat, Double(hue),Double(sat),Double(bright) ) } if color.getRed(&red, green: &green, blue: &blue, alpha: &alpha){ rgbLabel.text = String(format: rgbFormat, Double(red),Double(green),Double(blue)) red = red * 255 green = green * 255 blue = blue * 255 hexLabel.text = String(format:hexFormat,Int(red),Int(green),Int(blue)) } } }
We have three outlets for our label in this controller. We have one function with a parameter color. The method first sets the background color of the view We do some initialization for the getRed
and getHue
methods. These two methods use some very evil parameter types. We do this setup to make the safest use. Both methods return a Boolean
value, but we set up the parameters with &
to make the RGB and HSB values pass-through parameters and thus we can read their values after the method executes. We check if we did convert a color in each case, and if we did, make a string with the results.
Make the Master View Controller
Press Command-N. Make a Cocoa Touch Class named ColorTableViewController, subclassing UIViewController
. We’ll use our simplified dynamic table view again. Change the class code to this:
class ColorTableViewController: UITableViewController { let hueCount = 5 let saturationCount = 10 }
This changes the class to a UITableViewController
, and adds two constants. These two constants are the number of hues we will have and the number of saturation samples from that hue. We will tell the table view this through our two data source methods. Add the following code:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return hueCount } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return saturationCount }
Next we will use a shortened version of the same background color technique we used in the table view section to make a list of colors .
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let hue = CGFloat(indexPath.section) / CGFloat(hueCount) let saturation = 1.0 - CGFloat(indexPath.row) / CGFloat(saturationCount) let color = UIColor(hue: hue, saturation: saturation, brightness: 1.0, alpha: 1.0) let cell = tableView.dequeueReusableCellWithIdentifier( "cell", forIndexPath: indexPath) as UITableViewCell cell.backgroundColor = color return cell }
Finally, we select the colors. We need to access the detail, and we access it through the controllers property as before. Add the following code:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let cell = tableView.cellForRowAtIndexPath(indexPath) if let split = self.splitViewController { let controllers = split.viewControllers let detailViewController = controllers[controllers.count-1] as? ColorViewController detailViewController?.displayColor((cell?.backgroundColor)!) } }
In this case, we have no navigation controller. So the controller in the last spot on controllers is the controller we want. We don’t need topViewController
.
Add to the Storyboard
Go to the storyboard. Select the Root View Controller in the outline. In the identity inspector make its class ColorTableViewController. Select the table view’s cell. In the attributes inspector, change the identifier to cell. Drag a navigation item onto the top toolbar. Select the navigation item title and change the title to Master.
Select the view controller that is our detail. In the identity inspector, change the class to ColorViewController. Open the assistant editor. From the circle on each of the outlets drag to the appropriate label.
Select an iPad 2 in the simulator. Build and run. If the device is not in landscape, rotate the device with Command-Left Arrow. The master and detail will be side by side. Scroll through the colors and select a color on the master, and the info shows up in the detail.
Adding a Navigation Controller
Many times we will need a navigation controller for detail, since we may need to access other scenes from this root scene. In the storyboard, select the detail view controller scene. Select Editor>Embed In>Navigation controller. A navigation controller shows up between the split view controller and the view controller.
On the detail view, add a navigation item to to the space for the navigation bar. Title it Color Details.
In the didSelectrowAtIndexPath
,change
let detailViewController = controllers[controllers.count-1] as? ColorViewController
to
let detailViewController = controllers[controllers.count-1].topViewController as? ColorViewController
Now we have a navigation controller and want the top view controller. Build and run again. It still works the same.
Rotate back to portrait. It seems we cannot access the master. Swipe from left to right and it appears:
Swipe right to left to dismiss it. Stop the simulator and change over to an iPhone 6. Run again:
we get a screen with the toolbar for navigating to the master. Press the button and we get the master
However we get stuck. There is no way back.
Adding a Segue
We’ve used didSelectRowAtIndexPath
to select our information. That works for split view controllers on regular width devices. We will add segue from the table view cells to bring us to the detail. This will work on both compact and regular width devices. Go to the storyboard, and open up the document outline. Find the detail navigation controller and the cell for the table view.
Control-drag from cell to Navigation Controller. Select a Show Detail selection segue. In the attributes inspector, name it detail. Build and run. You will find that selecting a cell sends us back to the detail, though nothing happens.
Using prepareForSegue with Split Views
With the segue operational, the didSelectRowAtIndexPath
code gets eclipsed. What we did in that delegate we need to move over to a prepareForeSegue
method. Open the ColorTableViewController.swift
file. Add to the ColorTableViewController
code the following:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "detail"{ let detailNavigationController = segue.destinationViewController as? UINavigationController } }
This is the standard start to this method. We assign the proper view controller to the destination. Our destination is a navigation controller, not a view controller. We need the top view controller from the navigation controller. Add this below the detailNavigationController
assignment:
let detailViewController = detailNavigationController?.topViewController as ColorViewController We have the correct controller. Add this : let indexPath = (tableView.indexPathForSelectedRow())! let cell = tableView.cellForRowAtIndexPath(indexPath) detailViewController.displayColor((cell?.backgroundColor)!)
In didSelectRowforIndexPath
we had a parameter indexPath
which is the index for the selected cell. In prepareForSegue
, we get the index path from the indexPathForSelectedRow
method. Once we have an index path, we get the cell as we did before. We then call the detail’s method displayColor
with the background color of the cell to display the information in the detail. Build and run. Go to the master and select a color:
We now have colors.
Hiding and Showing the Master
Hiding and showing the master needs to be more under user control. Add to the end of the prepareforSegue
method this code:
detailViewController.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() detailViewController.navigationItem.leftItemsSupplementBackButton = true
The split view controller has a special bar button method displayModeButtonItem.
This method creates a special bar button for controlling the master. While it works in viewDidLoad,
it causes a crash on auto-rotation. It seems to work correctly only when added here. Using an iPhone 5s simulator, build and run. Look at the detail in portrait and landscape
The system makes a different button for each device in each orientation. It uses the most appropriate button look for that device.
In our last part about split view controllers, we’ll cover material from my original post — the disappearing outlet and what to do about it.
Leave a Reply