Tag Archives: iPad

Auto Layout and Size Classes: The Regular Size and Lining up Buttons Evenly

In the conclusion of our series on auto layout, we’ll discuss the regular size for iPads and one side of the iPhone 6 plus. We’ll make a line of evenly spaced buttons using auto layout, first for the iPad, then again for the iPhone 6 plus in landscape with its odd regular width, compact height size.

Transcript

Hello, welcome to our final installment in the beginning auto layout videos. I’m Steve from MakeAppPie.com

With size classes we’ve discussed there are three classes: Compact, Any and Regular.

We’ve already discussed most iPhones have compact sizes, and in the previous lessons set up a special arrangement in compact height for landscape with buttons on the side.

This lesson will be on the regular class. In its default settings, an iPad and an iPhone 6 plus in landscape have regular sized sides.

iPads have regular sizes on both sides. The iPhone 6 plus has one regular side and one compact side.

In our demo, we’ll continue working with the layout from the previous videos. We’d like to have side by side buttons just below the label in regular views. We’ll also make the Done button proportionally smaller than the rest of the buttons.

We would like to keep our buttons proportionally sized on different devices. However the actual size of the view changes. There are a few techniques in auto layout we will use to accomplish equally sized buttons.

The key to equally sized buttons is keeping the beginning and end buttons in a specific place. We then add constraints to keep aligned vertically and the same space away from every other button. Next, we make all the buttons the same size and height as the beginning button and specify the width and height as necessary for the beginning button. Our final step will be to make one button proportionally sized smaller than the rest.

Let’s do this for all regular width devices. Change the size class to regular width any height.

If you cannot see it, move the preview over to show the iPad preview.

Our vertical constraints for the text view are currently set to be 10 points from the Cheese button and 10 points from the Done button. Those buttons will be moving and will mess up our text view when moved, so we’ll constrain the top and bottom constraints to the container view’s margins instead.

Control drag the text view to the bottom space and click Bottom space to Bottom Layout Guide to make a new constraint for the bottom view.

We’ll also do the same to the top margin of the text view. Control drag from the text view to the top of the container. Select Top Space to Top Layout Guide.

Often it’s best to clear constrains instead of changing them when making big changes. Clear the constraints on the Done button

Clear the constraints on the cheese button as well.

We will need some space to put the done button. Resize the Cheese button by dragging the right handle over until there is some space for a done button. Don’t worry how much, we will be setting sizes later.

Move the Done button to its new position and resize it to generally fit. Again, we don’t have to be anywhere close to perfect, as the constraints will do that for us. You can use the recommended choices from the guides as they show up.

Control drag to the right and select Trailing Space to Container to anchor this to the right margin.

Once done, control drag from the Done button to the Pepperoni button. Shift select Top, Equal Heights and Equal Widths.

Since the Pepperoni button is already anchored and sized we don’t do anything with it. We now need to set the middle Cheese button. Control drag the Cheese button to the Pepperoni button. Shift select Horizontal Spacing, Top, Equal Heights, Equal Widths.

Now control drag from the cheese to the done button. Shift select Horizontal Spacing, and Top.

Update the frames. We now have three buttons of equal widths aligned.

We want the Done button to be smaller than the other buttons. Select the Done button and then click Edit for Equal Width to Pepperoni. Change the multiplier to 1:3.

And we’re done. It looks good on the iPad.

However the preview in iPhone 6 plus in landscape is a big mess. The regular width, compact height has too many constraints and can’t figure out what to do. Change the size class of the storyboard to regular width, compact height.

Select Done and you will see far too many constraints. This is the point that is best just to get rid of it all.

Select the text view, which also has too many constraints and clear its constraints. Move and resize the text view using the guides.

Let’s do the same for the Done button. Select the Done Button and clear its constraints. Move the Done button over to the right margin aligned with the other buttons.

Now let’s get the Cheese button cleaned up. Select and clear the constraints. Move the cheese button to the center.

To finish our cleanup, clear the constraints on the Pepperoni button. It is in the right position so we don’t have to move it.

Select the text view, and then the pin menu. add i-Beams on the bottom, left and right. Pin the left margin to 0 points, the right margin to 0 points, and the bottom to 20 points. Set the height to 240 points.

Select the Pepperoni button. Pin 10 points to the top,10 to the bottom, 0 to the left margin, and update constraints. Remember this is to the nearest views, so we are 10 from the label and 10 from the text view.

Select the Done button. Pin 10 points from the top 10 points to the bottom, 0 points to the right margin, and again update the constraints.

Select the Cheese button. Pin left 10 points and right 10 points but don’t update its constraints. There are placement errors at the moment but we will deal with them after we set the vertical alignment and size.

Control drag from Cheese to Pepperoni and select Top.

Control drag from Cheese to Pepperoni again. We already have horizontal spacing and top defined. Shift click equal widths equal heights.

Set the size of the done button. Control drag from Done to Pepperoni. Shift click equal widths and equal heights.

Now everything is set up correctly. On the resolve menu, click update all frames.

We still need to make the done button smaller. Like before, select the Done button. In the inspector click the Edit button on Equal Width to Pepperoni. Change the multiplier there to 1:3.

Going over to the preview pane, we see the iPhone 6 plus is now set up correctly.

We’ve now completed the size class and auto layout of this app’s storyboard. a general lesson to remember is to start with width any, height: any and work your way to more detailed size classes if you need to make changes.

Thank you for watching this short series on beginning auto layout and size classes. For more cool stuff on iOS app development head over to makeAppPie.com.

Swift Swift: Using Color and UIColor in Swift Part 1: RGB

Not so Cheap Sunglasses. Watercolor. 2008

[Updated to Swift 2.0/iOS9.0 9/18/15 SJL – errors corrected 11/20/15]
I’m color blind, and that makes me a very good person to teach you about color and UIColor.  I’ve painted for many years and my color blindness does not detract but actually helps me pick colors.  These two examples, one a water-color of mine from 2008 and another I did with the Crayon Style App for iPhone were each done in less than six colors. I want to give you the secrets of good colors that I learned the hard way.

PIllow. Crayon Style App 2014. from a photo reference.
PIllow. Crayon Style App 2014. from a photo reference.

I originally thought I would show you how to use color in one blog post, but it turned into two. In this post I’ll show you how to work with colors, how to use the RGB colors and how to apply them to controls in Swift with UIColor. In the next post I’ll show you how to make colorful, well designed displays with HSB.

The RGB  and RGBA Colors

There are several ways to  select colors. For what most people think of as color, there are two important color identifying systems: RGB and HSB. The most useful to most programmers is the RGB or Red-Green-Blue color system.   As far back as the time of the Apple ][, RGB colors are easy to communicate as bytes. They are also the standard color space for CSS, so any CSS web color scheme with a little work will translate into an app UI’s color scheme.

RGB traditionally has been a value between 0 and 255, and CSS uses this numbering pattern in one of two ways:

strong { color: #ff0000 }           /* #rrggbb */
strong { color: rgb(255,0,0) }      

Each of the color components can be expressed separately as a value between 0 and 255, with 255 being the full color and 0 being no color.  The first number is red, the second green and the third blue. The code line above is for the color red, since it is all red and no other color.  We can express red as a hexadecimal number #ff0000.  In hexadecimal 00 is 0 and ff is 255 in decimal.  We have three two digit numbers stuffed into one number. Xcode for the UIColor class decided to use a CGFloat between 1 and 0 instead of an integer between 0 and 255 .  The code to make a red color would be:

let myRedColor = UIColor(
    red:1.0,
    green:0.0,
    blue:0.0,
    alpha:1.0)

Notice for a UIColor there is a fourth attribute: A for alpha. Alpha levels are the amount of transparency of a color, or how much of the color underneath we can see. For our purposes in this post we will stick with solid colors, an alpha of 1.0. Feel free to experiment with translucency and transparency on your own.

System Colors

There is a set of often used system colors which have their own methods.  For example, instead of the assignment above,  we can type for red:

let myRedColor = UIColor.redColor()

With the same result. There are fourteen colors and a special color clearColor which is an alpha of 0 .

The Apple System Colors with a color wheel of RGB primary and secondaries.
The Apple System Colors with a color wheel of RGB primary and secondaries. Click on image for a better view.

In the illustration I organized the colors in a color wheel for the primary and secondary colors. A primary color is one we base all other colors. This for RGB  is not surprisingly red, green and blue. There are colors we can make by equally mixing two of the primaries. These are known as secondaries, and for RGB, those are Cyan, Magenta and Yellow, which also make up another color system CMYK which we’ll mention more in the next post.  Beneath them we have five different shades of gray.  Beneath that, three colors that don’t fit in a RGB scheme easily: Orange, Purple, and Brown.

Labels and Buttons: A Color Demo Application.

Let’s make a demonstration project. Open a new project in Xcode with a single view named SwiftColorDemo

Set up your storyboard to look something like this, with a button in the upper left and a label in the upper right:

2015-09-18_07-45-56

I set this up in autolayout. You can of course just drag and drop the controls, and adjust accordingly. For those who want to set up in auto layout, I pinned the button to the top and left margin of the view and the label to the top and right margin of the view. I made the label and button of equal widths and equal heights. Then I pinned the label and button 20 points horizontally from each other. I made the button one third of the height of the view by selecting equal heights between the superview and button, and then making the button have a multiplier of 1:3. I aligned the Label and Button labels with a pin to the view below them and aligned them to the leading edge.  For the sliders. I pinned then horizontally to the left and right margin. I took the top slider and aligned it to the vertical center, then pinned the other teo slider to it vertically. The segmented control I pinned to the bottom,left and right margins. If you don’t understand any of that, you might find my book Practical Autolayout very useful, though for this example you don’t need it.

Open the assistant editor. Make an outlet for the label by control-dragging from the label to the code in the assistant editor to make an outlet named mysampleColorLabel.

@IBOutlet weak var mySampleColorLabel: UILabel!

Getting to the code, in ViewController.swift, Add this to viewDidLoad:

override func viewDidLoad() {
    super.viewDidLoad()
//Set the label's color with the System colors
    mySampleColorLabel.backgroundColor = UIColor.blackColor() 
    mySampleColorLabel.textColor = UIColor.whiteColor()
 }

Here we set the label’s text color to white and background to black using system colors. Let’s set the background of the button to gray using a more explicit reference to red green and blue. Add this under the text color for the label:

//Set the button's back ground with a RGB specfied color
//integer and a float both work -- this is gray
mySampleColorButton.backgroundColor = UIColor(
    red: 0.5,
    green: 128/255.0,
    blue: 0.5,
     alpha: 1.0)

The parameters in the UIColor initializer are a CGFloat between 1 and 0. We can take a number and divide it by 255.0 to get a fraction that is between these two numbers, as we did with the green, since 128/255.0 equals 0.5. When you have a palette figured out in integer RGB values this is the way to specify values.
We have a background for a button but setting the text color is a little  different. Buttons are controls which have states. If we set the color by the textColor property of titleLabel, the system will ignore it. Add the following to your viewDidLoad code:

//the right way to set a color on a button title
mySampleColorButton.setTitleColor(
    UIColor.whiteColor(),
    forState: UIControlState.Normal)

When setting the titleColor of a button, use the method setTitleColor and for most cases set it for a state of UIControlState.Normal

Let’s set the background property of the view. It may help to have a light, neutral color to help us see the bolder colors we will play with. The color represented by #fde8d7 is a peach color and works nicely for our purposes. You’ll find many colors and color palettes described in hex numbers on websites like colourlovers.com or colourpod.com which feature pre-made palettes. But often such sites will  often use the hexadecimal system to describe the color. Swift fortunately can represent integers as hex values. Take the first two digits of the hex number as red, the next two as green, and the last two as blue. Write them with 0x  prefixed to the number Red for #fde8d7 would be 0xfd for example. Using this we can make our background peach like this:

//set the background color to #fde8d7
view.backgroundColor = UIColor(
    red: 0xfd/255,
    green: 0xe8/255,
    blue: 0xd7/255,
    alpha: 1.0)

Build and run. We have color!

Screenshot 2014-10-02 07.12.46

Making a Simple Color Picker

Lets hook up the sliders and the segment control to change the colors of the button and label.

We will need outlets. From the storyboard to the assistant editor, control drag from the button, label,sliders and segmented control to make more outlets:

@IBOutlet weak var mySampleColorButton: UIButton!
@IBOutlet weak var redSlider: UISlider!
@IBOutlet weak var greenSlider: UISlider!
@IBOutlet weak var blueSlider: UISlider!
@IBOutlet weak var backgroundTextSegment: UISegmentedControl!

Now control drag from the sliders to make three actions, one action for each slider, then add this code to those actions:

@IBAction func redSliderChanged(sender: UISlider) {
    displayColors()
}
@IBAction func greenSliderChanged(sender: UISlider) {
    displayColors()
}
@IBAction func blueSliderChanged(sender: UISlider) {
    displayColors()
}

The three sliders call a method displayColor. Add this to your code:

 //MARK: - instance methods
func displayColors(){
let red = CGFloat(redSlider.value)
let blue = CGFloat(blueSlider.value)
let green = CGFloat(greenSlider.value)
let color = UIColor(
    red: red,
    green: green,
    blue: blue,
    alpha: 1.0)
if backgroundTextSegment.selectedSegmentIndex == 0 { 
    mySampleColorButton.backgroundColor = color
    mySampleColorLabel.backgroundColor = color
} else {
    mySampleColorButton.setTitleColor(
        color,
        forState: .Normal)
    mySampleColorLabel.textColor = color
}
mySampleColorLabel.text = String(
    format: "%i,%i,%i",
    Int(red * 255),
    Int(green * 255),
    Int(blue * 255))
let myTitleText = String(
    format: "%6.2f,%6.2f,%6.2f",
    Float(red),
    Float(green),
    Float(blue))
mySampleColorButton.setTitle(
    myTitleText,
    forState: .Normal)
}

The first three lines of the function take the values from the three sliders and make them into a color. Sliders return Float and UIColor works in CGFloat. These are not the same type, Swift is very cranky about type so we need to convert red, green and blue with a CGFloat initializer before we do anything else. Next, we checks which segment the user selected. If it is the first segment, the color gets applies to backgrounds. If not, the text color changes. The final lies of code set the text on the label and button to show what color the sliders selected.

You can build and run this code. Now play with the sliders and the control to make pretty colors.

Screenshot 2014-10-02 07.14.20

Adding Color to the Slider Thumbs

It would be nice to add some color to the sliders. It would be even better if the tint color reflected the value of that color. The thumbTintColor property of UISlider does let us do that, but there is a catch: you need to initialize the tint with a image. Add the following code to the viewDidLoad method to initialize the view.

//initialize sliders for tints with an image
let sliderImage = UIImage(named: "color knob")
redSlider.setThumbImage(
    sliderImage,
    forState:UIControlState.Normal)
greenSlider.setThumbImage(
    sliderImage,
    forState:UIControlState.Normal)
blueSlider.setThumbImage(
    sliderImage,
    forState:UIControlState.Normal)

This of course needs an image. I made the following one, which you can download by right clicking on it and save the image as color knob.

color knob

In the project navigator, open the Assets.xcassets file. Drag the image file you saved from finder into the list of images.

Now change the code for the sliders to this:

@IBAction func redSliderChanged(sender: UISlider) {
    let red = CGFloat(sender.value)
    sender.thumbTintColor = UIColor(
        red: red,
        green: 0.0,
        blue: 0.0,
        alpha: 1.0)
    displayColors()
}

@IBAction func greenSliderChanged(sender: UISlider) {
    let green = CGFloat(sender.value)
    sender.thumbTintColor = UIColor(
        red: 0.0,
        green: green,
        blue: 0.0,
        alpha: 1.0)
    displayColors()
 }
@IBAction func blueSliderChanged(sender: UISlider) {
     let blue = CGFloat(sender.value)
     sender.thumbTintColor = UIColor(
         red: 0.0,
         green: 0.0,
         blue: blue,
         alpha: 1.0)
     displayColors()
}

We get the slider’s value, and convert it to a CGFloat. We take that value and change the tint value of the slider for that color only, leaving the other two colors at 0.
Build and run and you start with the button image. Move the buttons and the color changes.

colorslider1

The image is any image if you never show it at all. Many people use the app icon. If you don’t want to show the image at all, you can change the color immediately after setting the image like this:

//give the sliders the initial color
redSlider.thumbTintColor = UIColor(
     red: 0.5,
     green: 0.0, 
     blue: 0.0, 
     alpha: 1.0)
greenSlider.thumbTintColor = UIColor(
     red: 0.0,
     green: 0.5,
     blue: 0.0,
     alpha: 1.0)
blueSlider.thumbTintColor = UIColor(
     red: 0.0,
     green: 0.0,
     blue: 0.5,
     alpha: 1.0)

Build and run. The image is gone
Screenshot 2014-10-02 07.18.38

Reading a UIColor to RGB values

There are times we need to get back an RGB value from a UIColor. For example our application has a bit of a bug that requires it. When we change from background to text, we lose the color data we had and the screen goes blank. The sliders do not reflect the current color data. When we switch from Background to text or vice versa, it would be good to change the sliders to reflect that change. Control-drag from the storyboard to the assistant editor to make an action for the segmented control like this:

 @IBAction func backgroundTextSegment(sender: UISegmentedControl) {
    }

There is a UIColor method getRed, however it is a bit tricky to use. Add the following code to this new method:

 @IBAction func backgroundTextSegment(sender: UISegmentedControl) {
// Create variables
    var r:CGFloat = 0
    var g:CGFloat = 0
    var b:CGFloat = 0
    var a:CGFloat = 0
    var myColor = UIColor()
//select the color you want to read from segment data
    if  sender.selectedSegmentIndex == 0{
        myColor = mySampleColorLabel.backgroundColor!
    } else {
        myColor = mySampleColorLabel.textColor
    }
//read the color and if it exists, set the sliders
    if myColor.getRed(&r, green: &g, blue: &b, alpha: &a){
        redSlider.setValue(Float(r), animated: true)
        greenSlider.setValue(Float(g), animated: true)
        blueSlider.setValue(Float(b), animated: true)
    }
}

The method getRed returns a Bool that it successfully found a color. It then does something odd: it places the colors in the parameters for the method, but as the well-named, totally evil type UnsafeMutablePointer<CGFloat>. We don’t want to mess with these — trust me on this. However there is an easy way to avoid them. In front of our variable name we have an & character, which tells the compiler to share and load the parameter value back into the variable. If the method finds a color, we set the sliders with animation, converting the CGFloats into Float values.

Build and run, switch between background and text, and the slider thumbs move from one side of the slider to the next, since it reads colors from the black and white label.

There is a Lot More to Cover

When I originally started this post I thought it would be a quick one. It turns out there are three parts I wanted to cover. This post covered much of UIColor using RGB and assigning color to controls. I need two more posts to cover UIColor with gray, alpha values and UIColor using HSB, which will include color matching, making rainbow buttons and making a workable palette. I will continue color in the next post, and I’ll discuss gray and gray tone palettes and alpha values in the newsletter. If you have not signed up for the newsletter, subscribe here where I will weekly include some cool stuff I don’t post on the blog.

The Whole Code

//
//  ViewController.swift
//  SwiftColorPlay
//
//  Created by Steven Lipton on 9/28/14.
//  Copyright (c) 2014 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var mySampleColorButton: UIButton!
    @IBOutlet weak var mySampleColorLabel: UILabel!
    @IBOutlet weak var redSlider: UISlider!
    @IBOutlet weak var greenSlider: UISlider!
    @IBOutlet weak var blueSlider: UISlider!
    @IBOutlet weak var backgroundTextSegment: UISegmentedControl!

    let sliderImage = UIImage(named: &quot;color knob&quot;)

        //MARK: - Actions

    @IBAction func backgroundTextSegment(sender: UISegmentedControl) {

        var r:CGFloat = 0
        var g:CGFloat = 0
        var b:CGFloat = 0
        var a:CGFloat = 0
        var myColor = UIColor()
        if  sender.selectedSegmentIndex == 0{
            myColor = mySampleColorLabel.backgroundColor!
        } else {
            myColor = mySampleColorLabel.textColor
        }
        if myColor.getRed(&amp;r, green: &amp;g, blue: &amp;b, alpha: &amp;a){
            redSlider.setValue(Float(r), animated: true)
            greenSlider.setValue(Float(g), animated: true)
            blueSlider.setValue(Float(b), animated: true)
        }

    }

    @IBAction func redSliderChanged(sender: UISlider) {
        let red = CGFloat(sender.value)
        sender.thumbTintColor = UIColor(
            red: red,
            green: 0.0,
            blue: 0.0,
            alpha: 1.0)
        displayColors()
    }

    @IBAction func greenSliderChanged(sender: UISlider) {
        let green = CGFloat(sender.value)
        sender.thumbTintColor = UIColor(
            red: 0.0,
            green: green,
            blue: 0.0, 
            alpha: 1.0)
        displayColors()

    }
    @IBAction func blueSliderChanged(sender: UISlider) {
        let blue = CGFloat(sender.value)
        sender.thumbTintColor = UIColor(
            red: 0.0,
            green: 0.0,
            blue: blue,
            alpha: 1.0)
        displayColors()

    }

    //MARK: - instance methods
    func displayColors(){
        //create a color from the sliders.
    let red = CGFloat(redSlider.value)
    let blue = CGFloat(blueSlider.value)
    let green = CGFloat(greenSlider.value)
    let color = UIColor(
        red: red,
        green: green,
        blue: blue,
        alpha: 1.0)
        //apply the color to the background or text
    if backgroundTextSegment.selectedSegmentIndex == 0 {
        mySampleColorButton.backgroundColor = color
        mySampleColorLabel.backgroundColor = color
    } else {
         mySampleColorButton.setTitleColor(
             color,
             forState: .Normal)
         mySampleColorLabel.textColor = color
        }
    mySampleColorLabel.text = String(
         format: &quot;%i,%i,%i&quot;,
         Int(red * 255),
         Int(green * 255),
         Int(blue * 255))
    let myTitleText = String(
         format: &quot;%6.2f,%6.2f,%6.2f&quot;,
         Float(red),
         Float(green),
         Float(blue))
    mySampleColorButton.setTitle(
         myTitleText,
         forState: .Normal)
    }

    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        //Set the label's color with the System colors
        mySampleColorLabel.backgroundColor = UIColor.blackColor()
        mySampleColorLabel.textColor = UIColor.whiteColor()

        //Set the button's back ground with a RGB specfied color
        // a hex number, and decimal and a 1-0 all work -- this is gray
        mySampleColorButton.backgroundColor = UIColor(red: 0x80/255, green: 128/255.0, blue: 0.5, alpha: 1.0)

        //This is syntactically correct but does nothing
        mySampleColorButton.titleLabel?.textColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)

        //the right way to set a color on a button title
        mySampleColorButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
        mySampleColorButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Disabled)

        //set the background color to #fde8d7
        view.backgroundColor = UIColor(red: 0xfd/255, green: 0xe8/255, blue: 0xd7/255, alpha: 1.0)

        //initialize sliders for tints with an image
        let sliderImage = UIImage(named: &quot;color knob&quot;)
        redSlider.setThumbImage(sliderImage, forState:UIControlState.Normal)
        greenSlider.setThumbImage(sliderImage, forState:UIControlState.Normal)
        blueSlider.setThumbImage(sliderImage, forState:UIControlState.Normal)

    }

}

The Swift Swift Tutorial: How to Use Split View on iPad (Round 1)

Back in July of 2014, I wrote this first post on using UISplitViewController in Swift. I gave how to use Apple’s template, which has a few interesting quirks, such as ARC killing@IBOutlets at the drop of a hat. I said I would write another post on the subject. I ended up making this section 2 of Chapter six of my Book Swift Swift: UI View Controllers . 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.It came out as three projects, which if you want and updated version without ARC killing your detail’s outlets, go here to start the series: Swift Swift: Split View Controllers (Round Two) Part 1: The Master-Detail template For more previews and interesting thoughts, sign up for my newsletter. –Steve

Two and a half weeks ago I had a request from a reader to cover split views and split view controllers in Swift. My apologies for taking this long, but now I know the reason for this user’s request. For those not familiar with split view controllers, they are a view controller meant for the iPad. They take two view controllers and place them in the same window. On the left  is the master, and is almost always a table view controller. The other view controller is known as the detail, which is a standard UIView controller.

Portrait Split Image View
Portrait Split Image View

I will admit I had a lot of problems implementing split views in Swift. I finally did get it to work. There is a change in Swift’s implementation that requires a bit of getting used to. This will be the first round of split views. In this post, I’ll show how to use the master-detail template to get them to work. After I cover a few other topics which will be useful in a more advanced version, I’ll revisit split views in Swift.

The Problem with the Detail Controller

The way I learned to make a split view with Objective-C was by dragging out a split view controller on a blank storyboard, and writing everything myself. That doesn’t seem to work too well and I was getting one of Swift’s most common error messages: fatal error: Can't unwrap Optional.None This is the run-time error when you unwrap a nil value from an optional.  This has to do with very weak references. The error happens in @IBOutlets in the DetailViewController . Outlets are by definition weak, and by Swift’s definition anything weak is also an optional. According to Apple’s book Using Swift with Cocoa and Objective-C. but @IBOutlet var name:Type is all we need to write. The compiler assumes the rest for us and defines an outlet as @IBOutlet weak var name: Type! = nil

Outlets in the detail controller cannot be directly accessed like an outlet hooked up anywhere else on a storyboard. In Swift, outlets are so weak in this case they are usually nil. Somehow the meager connections to the split view controller are not enough to keep them alive, though with the equivalent code in Objective-C there are enough strong pointers. In Swift, if you try something simple like this in the detail:

pizzaLabel.text = "Veggie"

That run-time error fatal error: Can't unwrap Optional.None. appears.

Apple’s Master-Detail template does work however.  There is a way to do this. In this lesson, we’ll set up the template to make a split view for PizzaDemo. We’ll cut and paste a lot of code from our last post on table views, and add a few new parts for the Split controller. As an iPad application, we will change our UI a bit from the iPhone. We’ll make our master the list of pizza types, and replace the segmented control with the master. In this session, we will not change the prices, but just get the data displayed in the model.

Get the template

In Xcode start a new project on the welcome screen, press command-shift-N or go to File>New>Project… Select in the iOS Application the Master-Detail Application. Name the app SwiftSplitPizzaDemo select Swift for the language and iPad for the device. Keep Core Data unchecked.

Select your file location and click Create. In the files created click the story board and look at what Xcode loaded for you.

The split view controller splits into two navigation controllers. One navigation controller has a UITableViewController subclass found in MasterViewController.swift. The other navigation controller has a UIViewController  subclass, found in DetailViewController.swift.

Xcode provides some sample code in the template to run an small application. Change to the iPad2 simulator, since it will fit on any screen nicely. Build and run. You get a pretty blank screen with a label on it. On your keyboard press the command key and the left or right arrow keys together a few times to rotate the ipad. As you change the orientation, the screen changes. Go back to portrait, and swipe right. the master view slides out. Press the plus button in the corner of the master view to add to the table. Rotate to a landscape view and hit plus again. Now select one of your two selections in the table view. The label in the detail changes.

We know what is there works. Now to replace this code with our own. Go to the storyboard. Start by selecting the dynamic table cell, changing the table cell to tableCell in the storyboard.  Also change the cell style to right detail.

Add the Model

Open the PizzaDemo application with the table view in it from our last lesson, if you want to copy and paste from there. If you do not have it, you can download it from github or you can copy what I have below.

We first need a Model. if you have the PizzaDemo code open, drag the Pizza.swift file over to SwiftSplitPizzaDemo. make sure Copy items if needed has a check next to it, and hit Finish. If you do not have the PizzaDemo open and don’t want to load it, hit command-N on the keyboard, and make a Cocoa Touch class called Pizza in Swift which subclasses NSObject. In the new Pizza.swift file, add this code:

import Foundation
/* --------

Our model for MVC
keeps data  and calculations
about pizzas

------------*/

class Pizza {
    var pizzaPricePerInSq = ["Cheese": 0.03 , "Sausage": 0.06 , "Pepperoni": 0.05 , "Veggie": 0.04]
    var typeList:Array {  //computed property 7/7/14
    get{
        return Array(pizzaPricePerInSq.keys)
    }
    }

    let pi = 3.1415926

    var pizzaDiameter = 0.0
    let maxPizza = 24.0
    var pizzaType = "Cheese"

    var radius : Double {  //computed property
    get{   //must define a getter
        return pizzaDiameter/2.0
    }
    set(newRadius){ //optionally define a setter
        pizzaDiameter = newRadius * 2.0
    }
    }

    var area :  Double {
    get{
        return pizzaArea()
    }
    }

    func pizzaArea() -> Double{
        return radius * radius * pi
    }

    func unitPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
        if unitPrice != nil {
            return unitPrice!
        }                               //optional type ?Double checking for nil
        else {
            return 0.0
        }
    }

    func pizzaPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
        if unitPrice != nil {                                   //optional type ?Double checking for nil
            return pizzaArea() * unitPrice!             //unwrapping the optional type
        }
        return 0.0
    }

    func diameterFromString(aString:NSString) -> Double {
        switch aString {
        case "Personal":
            return 8.0
        case "10\"":
            return 10.0
        case "12\"":
            return 12.0
        case "16\"","15\"":
            return 16.0
        case "18\"":
            return 18.0
        case "24\"":
            return 24.0
        default:
            return 0.0
        }
    }

}

Add the Master

Go to the MasterViewController.swift file. Go into the MasterViewController class and comment out the following line:

var objects = NSMutableArray()

This will produce a lot of error messages. We will need to change the code in each place there is an error message. To add our model, just under where you commented out, objects add:

var pizza = Pizza()

Add the TableView Code

Scroll down to where the table view data source is:

   //MARK:  - Table View

    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        return objects.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

        let object = objects[indexPath.row] as NSDate
        cell.textLabel.text = object.description
        return cell
    }

Replace it with the following code.

//MARK: -Table View
    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        // Return the number of rows in the section.
        return pizza.pizzaPricePerInSq.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell
        let row = indexPath!.row   //get the array index from the index path
        let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as UITableViewCell  //make the cell
        let myRowKey = pizza.typeList[row]  //the dictionary key
        cell.textLabel!.text = myRowKey
        let myRowData = pizza.pizzaPricePerInSq[myRowKey]  //the dictionary value
        cell.detailTextLabel!.text = String(format: "%6.3f",myRowData!)
        return cell
    }

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
        return 44.0
    }

We will not be editing the table. Comment out this method for insertion:

/*
    func insertNewObject(sender: AnyObject) {
        if objects == nil {
            objects = NSMutableArray()
        }
        objects.insertObject(NSDate.date(), atIndex: 0)
        let indexPath = NSIndexPath(forRow: 0, inSection: 0)
        self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }
*/

Also comment out this method for deleting rows:

/*
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            objects.removeObjectAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
        }
    }
*/

Disable editing of the table by returning false here:

    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return false
    }

We just made a table. We covered how to do that last time. Here we begin to see something new. Change tableview(didSelectRowAtIndexpath:indexPath:) to this:

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //let object = objects[indexPath.row] as NSDate
        //self.detailViewController!.detailItem = object
        pizza.pizzaType = pizza.typeList[indexPath.row] //set to the selected pizza
        if (detailViewController != nil){
            self.detailViewController!.detailItem = pizza //send the model to the detailItem
        }

    }

Looking at the top of our code the master view controller makes an instance of the DetailViewController class. Here we set the detailItem property of the detail view controller, whose type is AnyObject!. This is key to everything, but we’ll discuss the property more in a few minutes. We set the pizzaType property to the selected pizza, and then send the model to the detail controller via this property.

To be consistent, handle an ios8 issue, and to get rid of the last error, change prepareForSegue() like this:

 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "showDetail" {
            let indexPath = self.tableView.indexPathForSelectedRow()
            //    let object = objects[indexPath.row] as NSDate
            //((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = object
            pizza.pizzaType = pizza.typeList[indexPath!.row] //set to the selected pizza
            ((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = pizza
        }
    }

Line 7 will not make a lot of sense if you are unfamiliar with split view controllers. Take a look at the storyboard:

The storyboard
The storyboard

One of a split view controller’s properties is an array called viewControllers. At most it has two objects,  navigation controllers for the master and the detail views. Embedded in each of these navigation controllers is a master and detail view. For the segue, we go from the master controller to the navigation controller, which is the segue’s destination. The first view controller in the destination navigation view controller’s array of controllers is the detail view. We access that by ((segue.destinationViewController as UINavigationController).topViewController Once we have the detail view controller, we assign the model to the detailItem property.

Now change viewDidLoad to this:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
/* Removed code for editing and use of bar buttons.
        self.navigationItem.leftBarButtonItem = self.editButtonItem()

        let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
        self.navigationItem.rightBarButtonItem = addButton
*/
        let controllers = self.splitViewController!.viewControllers
        self.detailViewController = controllers[controllers.endIndex-1].topViewController as? DetailViewController

        }

Lines 10 and 11 set up the detail view controller in the master. It takes the detail view from the viewControllers array we assigned to controllers We also removed the bar button item navigation bar stuff since we will not be using it in this tutorial.

We now have a completed master view controller. Essentially it is a table view with some odd connections to a property in the detail view controller. Build and run. In portrait swipe from left to right. Try rotating the iPad and see what happens.

Add the Detail

The next step is to design the detail view controller. We will be showing a lot more information on this scene, so a good layout is important.

Design and Connect the Scene

Go into the storyboard and go to the detail view controller there. Add five labels  with the text Pizza:, Size:, Anchovy, 12″, and Pizza Info And Price. Add one button with a blue background and white foreground labeled 10″. Copy the button five times. Resizing as necessary, lay out and label everything something like this:

Layout for the detail view

I did use auto layout here since we will be rotating the iPad.

Open the assistant editor to the detail view controller and add the following outlets by control dragging from the Anchovy, 12″, and Pizza Info And Price labels on the storyboard.

    @IBOutlet var pizzaSizeLabel: UILabel!
    @IBOutlet var pizzaTypeLabel: UILabel!
    @IBOutlet var pizzaPriceLabel: UILabel!

Adding Code for the DetailViewController

first, add the model to the view

var pizza = Pizza()

Next, add this code for the size buttons.

@IBAction func pizzaSizeButton(sender: UIButton) {
pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel.text)
configureView()
}

Drag from the circle to each of the buttons to connect them all to this method.

Take a look at this code:

    var detailItem: AnyObject? {
        didSet {
            // Update the view.
            self.configureView()
//comment out this if clause  if you want the master to not disappear.
            if self.masterPopoverController != nil {
                self.masterPopoverController!.dismissPopoverAnimated(true)
            }
        }
    }

Lines 1-4 are at the heart of the way we get outlets to work. The MasterViewController instantiated a detailViewController. The master then set this property detailItem with the model, which is the data we wanted to pass to the detail controller. While just displaying is not strong enough a connection to keep the outlets alive, this connection is. To use an outlet we refer to detailItem. When set, detailItem calls configureView(), which looks like this:

    func configureView() {
        // Update the user interface for the detail item.
        if let detail: AnyObject = self.detailItem {
            if let label = self.detailDescriptionLabel {
                label.text = detail.description
            }
        }
    }
  

This is Apple’s template code to put text on a label. In line 2 we do two things at once. First we assign detailItem to a new constant detail. If that is successful, the app moves on to assigning another variable label to a UILabel. If that is successful, the app uses the assigned label to change the label properties. We sneakily assigned a strong pointer to a bunch of weak ones, at least for the life of the method. This is the way to access IBOutlets. Replace this code with the following:

     func configureView() {
        // Update the user interface for the detail item.
        if let detail = self.detailItem as? Pizza {     //if we have an object in the detail item
            pizza = detail
            if let label = self.pizzaTypeLabel {
                label.text = detail.pizzaType
            }
            let pizzaSizeString = NSString(format:"%6.1fin Pizza",detail.pizzaDiameter)
            let priceString = NSString(format:"%6.2f sq in at $%6.2f is $%6.2f",detail.pizzaArea(),detail.unitPrice(),detail.pizzaPrice())
            if let label = self.pizzaSizeLabel{
                label.text = pizzaSizeString
            }
            if let label = self.pizzaPriceLabel{
                label.text = priceString
            }

        }

    }

A few things changed  in configureView() for this example. In line 2, since there is a single detail view,  detailItem receives only the model.  I cast this directly to a instance of Pizza. I assign this to the model in the detail controller. While the outlets can be set here in configureView() everything else won’t be. If we want the properties from the buttons to work, we need to set the model here. After setting detail, we follow the if let label= pattern for updating our labels. That’s the trick to getting outlets to work in split views.

The Model initializes to the same values on both controllers, head over to the master view controller’s viewDidLoad() and add the following line:

pizza.pizzaPricePerInSq["Pepperoni"] = 9.99

This places a marker to prove our connections work.
Build and run. Play around with the app in different orientations.
splitview demo 4

There’s Always One More Bug

If you try to change the size of a pizza before selecting one in the master view, you’ll find nothing happens. We need to run configureView() in the detailViewController to get it to work. Go back to the MasterViewController‘s viewDidLoad() and add the following:

//send the model to the detailItem
        if (detailViewController){
            self.detailViewController!.detailItem = pizza

The pizza = detail assignment buried in line 4 of configureView() connects up enough to get the buttons to work.

This code works, and using the master-detail template is easy, though cumbersome, once you know the trick I showed here. There are still a lot of unanswered questions. We’ll explore some of those in the upcoming installments.

The Whole Code

Here is the code for the model, master view controller and detail view controller for copying and review. You can find a copy of the completed  project at Github as well.

Pizza.Swift

//
//  Pizza.swift
//  pizzaDemo
//
//  Created by Steven Lipton on 7/1/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

import Foundation
/* --------

Our model for MVC
keeps data  and calcualtions
about pizzas

------------*/

class Pizza {
    var pizzaPricePerInSq = ["Cheese": 0.03 , "Sausage": 0.06 , "Pepperoni": 0.05 , "Veggie": 0.04]
    var typeList:Array {  //computed property 7/7/14
    get{
        return Array(pizzaPricePerInSq.keys)
    }
    }

    let pi = 3.1415926

    var pizzaDiameter = 0.0
    let maxPizza = 24.0
    var pizzaType = "Cheese"

    var radius : Double {  //computed property
    get{   //must define a getter
        return pizzaDiameter/2.0
    }
    set(newRadius){ //optionally define a setter
        pizzaDiameter = newRadius * 2.0
    }
    }

    var area :  Double {
    get{
        return pizzaArea()
    }
    }

    func pizzaArea() -> Double{
        return radius * radius * pi
    }

    func unitPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
        if (unitPrice != nil) {
            return unitPrice!
        }                               //optional type ?Double checking for nil
        else {
            return 0.0
        }
    }

    func pizzaPrice() ->Double{
        let unitPrice = pizzaPricePerInSq[pizzaType]    //optional type ?Double
        if (unitPrice != nil) {                         //optional type ?Double checking for nil
            return pizzaArea() * unitPrice!             //unwrapping the optional type
        }
        return 0.0
    }

    func diameterFromString(aString:NSString) -> Double {
        switch aString {
        case "Personal":
            return 8.0
        case "10\"":
            return 10.0
        case "12\"":
            return 12.0
        case "16\"","15\"":
            return 16.0
        case "18\"":
            return 18.0
        case "24\"":
            return 24.0
        default:
            return 0.0
        }
    }

}

MasterViewController.swift

//
//  MasterViewController.swift
//  SwiftSplitPizzaDemo
//
//  Created by Steven Lipton on 7/17/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

import UIKit

class MasterViewController: UITableViewController {

//MARK: Life Cycle
    var detailViewController: DetailViewController? = nil
    //var objects = NSMutableArray()
    var pizza = Pizza()

    override func awakeFromNib() {
        super.awakeFromNib()
        self.clearsSelectionOnViewWillAppear = false
        self.preferredContentSize = CGSize(width: 320.0, height: 600.0)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
/* Removed code for editing and use of bar buttons.
        self.navigationItem.leftBarButtonItem = self.editButtonItem()

        let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
        self.navigationItem.rightBarButtonItem = addButton
*/
        let controllers = self.splitViewController!.viewControllers
        self.detailViewController = controllers[controllers.endIndex-1].topViewController as? DetailViewController

        pizza.pizzaPricePerInSq["Pepperoni"] = 9.99

        //send the model to the detailItem
        if (detailViewController != nil ){
            self.detailViewController!.detailItem = pizza
        }

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
/*
    func insertNewObject(sender: AnyObject) {
        if objects == nil {
            objects = NSMutableArray()
        }
        objects.insertObject(NSDate.date(), atIndex: 0)
        let indexPath = NSIndexPath(forRow: 0, inSection: 0)
        self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
    }
*/
//MARK: - Segues

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "showDetail" {
            let indexPath = self.tableView.indexPathForSelectedRow()
            //    let object = objects[indexPath.row] as NSDate
            //((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = object
            pizza.pizzaType = pizza.typeList[indexPath!.row] //set to the selected pizza
            ((segue.destinationViewController as UINavigationController).topViewController as DetailViewController).detailItem = pizza
        }
    }
//MARK:  - Table View Delegates
    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        // #warning Potentially incomplete method implementation.
        // Return the number of sections.
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete method implementation.
        // Return the number of rows in the section.
        return pizza.pizzaPricePerInSq.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
        //note I did not check for nil values. Something has to be really broken for these to be nil.
        let row = indexPath!.row   //get the array index from the index path
        let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath!) as UITableViewCell  //make the cell
        let myRowKey = pizza.typeList[row]  //the dictionary key
        cell.textLabel!.text = myRowKey
        let myRowData = pizza.pizzaPricePerInSq[myRowKey]  //the dictionary value
        cell.detailTextLabel!.text = String(format: "%6.3f",myRowData!)
        return cell
    }

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 44.0
    }

    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return false
    }
/*
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            objects.removeObjectAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
        }
    }
*/
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        //let object = objects[indexPath.row] as NSDate
        //self.detailViewController!.detailItem = object
        pizza.pizzaType = pizza.typeList[indexPath.row] //set to the selected pizza
        if (detailViewController != nil){
            self.detailViewController!.detailItem = pizza //send the model to the detailItem
        }

    }

}

DetailViewController.Swift

//
//  DetailViewController.swift
//  SwiftSplitPizzaDemo
//
//  Created by Steven Lipton on 7/17/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//

import UIKit

class DetailViewController: UIViewController, UISplitViewControllerDelegate {

    @IBOutlet var pizzaSizeLabel: UILabel!
    @IBOutlet var pizzaTypeLabel: UILabel!
    @IBOutlet var pizzaPriceLabel: UILabel!
    @IBOutlet var detailDescriptionLabel: UILabel!
    var pizza = Pizza()

    var masterPopoverController: UIPopoverController? = nil

    var detailItem: AnyObject? {
        didSet {
            // Update the view.
            self.configureView()

            //Comment out this if clause if you don't want the Master to disappear.
             if self.masterPopoverController != nil {
                self.masterPopoverController!.dismissPopoverAnimated(true)
            }
        }
    }

    @IBAction func pizzaSizeButton(sender: UIButton) {
         pizza.pizzaDiameter = pizza.diameterFromString(sender.titleLabel!.text!)
        configureView()
    }

    func configureView() {
        // Update the user interface for the detail item.
        if let detail = self.detailItem as? Pizza {     //if we have an object in the detail item
            pizza = detail
            if let label = self.pizzaTypeLabel {
                label.text = detail.pizzaType
            }
            let pizzaSizeString = NSString(format:"%6.1fin Pizza",detail.pizzaDiameter)
            let priceString = NSString(format:"%6.2f sq in at $%6.2f is $%6.2f",detail.pizzaArea(),detail.unitPrice(),detail.pizzaPrice()) //added 6/29/14
            if let label = self.pizzaSizeLabel{
                label.text = pizzaSizeString
            }
            if let label = self.pizzaPriceLabel{
                label.text = priceString //added 6/29/14
            }

        }

    }

    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.configureView()
    }

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

    //MARK: - Split View

    func splitViewController(splitController: UISplitViewController, willHideViewController viewController: UIViewController, withBarButtonItem barButtonItem: UIBarButtonItem, forPopoverController popoverController: UIPopoverController) {
        barButtonItem.title = "Master" // NSLocalizedString(@"Master", @"Master")
        self.navigationItem.setLeftBarButtonItem(barButtonItem, animated: true)
        self.masterPopoverController = popoverController
    }

    func splitViewController(splitController: UISplitViewController, willShowViewController viewController: UIViewController, invalidatingBarButtonItem barButtonItem: UIBarButtonItem) {
        // Called when the view is shown again in the split view, invalidating the button and popover controller.
        self.navigationItem.setLeftBarButtonItem(nil, animated: true)
        self.masterPopoverController = nil
    }

}