Tag Archives: swift

Parsing Strings from Time and Fractions to Doubles

This week’s lesson covers a topic the I left off on last week’s lesson, but can be used outside that context. I thus wanted to cover it separately. Last week I showed you how to use a UIPickerView to input numbers with a lot less validation. We looked at doubles, fractions and time intervals.

2016-12-12_07-33-06  2016-12-12_07-35-25     2016-12-12_07-34-30

You can go here to read the article on how I set that up. I left the article with one deficiency: the values returned from the picker view are strings, not numbers. In this lesson, I’ll show you how to convert the string to the much more useful Double and its alias TimeInerval. Since that’s  something you can use outside the application from last week, I’m presenting it separately. Those who want this  in the picker view should be able to cut and paste these functions into the code from last week.

For this Lesson I’m going to use a swift playground. You can use one in Xcode or on your iPad by downloading this file and loading it into playgrounds: .  I’ve heavily commented that file to explain what’s going on here.

Converting a String to Double.

The easiest of the conversion cases will also be the building block of all the rest: converting a string to a double. In a new playground, add this:

let numberString = "3.1415926"
if let number = Double(numberString){
    print(number)
}

The constructor for type Double has a converter built-in for converting strings to doubles. All you need is Double("3.14") to convert the string to a number – almost. Double returns an optional value of Double if you convert a string, where nil is an invalid string for conversion. for 3.l4l59@6 instead of 3.1415926. returns nil. Before you use the value, you’ll need to unwrap it. For these conversions,  I use either if let or guard to do that, so I can return nil if there is any reason the string is invalid for conversion. Here, I ignore any nil case, but that will change shortly.

Converting Minutes and Seconds to TimeInterval

TimeInterval is a commonly used type, which is really an alias for Double. TimeInterval is different from the other time type Date you’ll often see in cod . TimeInterval is a measure of seconds, independent of a starting and ending time.  Date is a  time based on a reference date. Both have their uses. Apple has the DateFormatter classes and its dateFromString method for handling dates, so I’m not concerned with those as much as TimeInterval. Unlike Date which has a lot of localization issues, almost everyone expresses the measures involved in time intervals constantly  to  hh:mm:ss.ss. The only variations are leaving off the hours or adding days. That means writing a straightforward function is relatively easy. I’ll start even easier and convert a string of minutes and seconds only. Make a new function in the playground of this:

func minutesSecondsInterval(_ timeString:String)->TimeInterval!{
}

We’ll assume that the string timeString looks like mm:ss.ss. If that’s the case, then there’s a very handy string function components(separatedBy:) change the function to this:

func minutesSecondsInterval(_ timeString:String)->TimeInterval!{
    let time = timeString.components(separatedBy: ":")
}

The components(separatedBy:) function creates a string array separated by a colon. if timeString was “12:34.56” time becomes [“12″,”34.56”]. I can take the components of that array and after checking for a non-nil value, add them together to find the number of seconds.

func minutesSecondsInterval(_ timeString:String)->TimeInterval!{
    var time = timeString.components(separatedBy: ":")
    if let seconds = Double(time[1]){
        if let minutes = Double(time[0]){
            return (seconds + (minutes * 60.0))
        }
    }
    return nil
}

If minutes and seconds are true values, I’ll add the seconds to minutes multiplied to 60 seconds. If either are nil, I drop out of the if and return nil.

A Flexible TimeInverval Converter

That’s okay, but not great. If I want hours, I’d need to write a new function. A string with more than one colon gives wrong results. If I put 1:10:12.5, the time array would be [“1″,”10″,”12.5”], adding 1 minute time 60 for 60 seconds and 10 seconds together for 70 seconds, which is wrong. This should be a lot more robust.

Think this out. Where hours, minutes and seconds appear in the array changes depending on the string. Reverse the array elements though, and if they exist, the time component is always in the same position in the array. 10:12.15 is [“12.15”,”10] reversed and 1:10:12.15 is [“12.15″,”10″,”1”] reversed. Seconds is always index 0, minutes index 1, hours index 2, if it exists. I multiply the number of seconds in a minute (60) and the number of seconds in an hour (3600) to the component before I add it to the result. If I have a matching array of those component multipliers, I could do that and put the whole thing in a loop that loops only the length of the array. If I have two components only do minute and seconds, If I have three, do all three. If I find five, return nil, because that’s an error. That all becomes this function:

func timeInterval(_ timeString:String)->TimeInterval!{
    let timeMultipilers = [1.0,60.0,3600.0] //seconds for unit
    var time = 0.0
    var timeComponents = timeString.components(separatedBy: ":")
    timeComponents.reverse()
    if timeComponents.count > timeMultipilers.count{return nil}
    for index in 0..<timeComponents.count{
        guard let timeComponent = Double(timeComponents[index]) else { return nil}
        time += timeComponent * timeMultipilers[index]
    }
    return time
}

I have a constant array timeMultipliers with the multiplier value for the component. This could be expanded to days, if I add another element of 86400.00, but I rarely need that for time intervals. I initialize a value time where I’ll add the components together. I break apart the timeString argument into an array then reverse it with the reverse() method of Array. I check the array if there are more components than I have multipliers for. If there is, it’s an invalid string, and I return nil.

There’s a loop from 0 to the last value in the timeComponents array. I use guard to convert the element in timeComponents to a Double, making sure the value is non-nil. If nil, I return nil. If not nil, I multiply by the multiplier, and add that result to time. When the loop is over, I return the time.

This will work with a value in seconds, seconds and minutes, and hours, seconds, and minutes, returning nil for any invalid answer.

Converting Fractions.

In the picker view, I made input for fractions. Fractions have three components: a whole number, a numerator and a denominator. The double value is the whole number added to the numerator divided by the denominator. In the picker view, I picked a format of w n/d, so thirty-three and a third is a string 33 1/3. This has two separators, a space and a slash instead of the single separator of the time interval. The String method you’ve used so far uses a single character. It also can use a character set. Add this function to your code:

func double(fractionString:String)->Double!{
    let separators = CharacterSet(charactersIn: " /")
    let components = fractionString.components(separatedBy: separators)
}

Before breaking the string apart to an array, you make a list of separators as a CharacterSet, in our case a space and a slash. This breaks the array into three components ["w","n","d"]. So the string “33 1/3” becomes ["33',"1","3"]. This never has a change of format, so I can directly use these values, and assume there are only three components, so check for a count of 3 in the array for validity. Get the components, then do the math to get the double.

func double(fractionString:String)->Double!{
    let seperators = CharacterSet(charactersIn: " /")
    let components = fractionString.components(separatedBy: seperators)
    if components.count == 3{
        if let wholeNumber = Double(components[0]){
            if let numerator = Double(components[1]){
                if let denominator = Double(components[2]){
                    return wholeNumber + (numerator/denominator)
                }
            }
        }
    }
    return nil //failure case
}

Try this out and you’ll get some doubles

One more bug

However, there’s a problem. Try this one:

double(fractionString: "12 0/5")

You should get 12.0 back. You get nil instead.
In cases where we don’t have three components, this doesn’t work. If I had two or one component, I’d like to return just the whole number and ignore whatever is wrong with the fraction. The if let optional chaining presents a problem though. All my calls are local, and make it hard to return just the whole number. This is the beauty of guard. I’ll change this code to use guard, check for the proper number of components and act accordingly.

Make a new function like the first but chage the parameter to (fraction fractionString:String) so we can use it in the playground without duplication complaints from the compiler.

func double(fraction fractionString:String)->Double!{
    let separators = CharacterSet(charactersIn: " /")
    let components = fractionString.components(separatedBy: separators)
}

I’m breaking this into two steps instead of one. I’ll check for components to be in the range of 1 to 3. of it isn’t we have an invalid string and will return nil. I’ll use guard to get a constant number. However since this is within the if clause it’s local, so if successful, I’ll assign to a variable wholeNumber the value of number

var wholeNumber:Double = 0.0
if components.count <= 3 && components.count > 0 {
     guard let number = Double(components[0]) else{
          return nil //invalid whole number
     }
     wholeNumber = number
} else {
     return nil // wrong number of components
}

Anything that survives that first if clause is a valid whole number and there are 1, 2, or 3 elements in the array. If I have 3 elements, as I did in the previous example, I have a mixed fraction, and can find the value of the numerator and denominator once again using guard, return the whole number if the value is invalid. Then I can return the value of the fraction, like I did in the last example.

if components.count == 3{
     guard  let numerator = Double(components[1]) else {return wholeNumber}
     guard  let denominator = Double(components[2]) else {return wholeNumber}
     if denominator != 0{
          return wholeNumber + (numerator/denominator)
     } else {return wholeNumber} //division by zero will result in zero for the fraction
}
return wholeNumber

You’ll notice my other paranoid thing I did. I prevented division by zero, returning the whole number if denominator is zero. I also return wholeNumber if I have only one or two components.

Test this out:

double(fraction: "33 1/3")
double(fraction: "33 0/3")
double(fraction: "33 1/0")
double(fraction: "33")

You get an extra added feature. Since the code converts everything to Double, this works:

double(fraction: "33.1234")

And so does this.

double(fraction: "33.1234 1.1/1.1")

Since it doesn’t harm anything and might be useful in a few places where I might be converting just decimals in one string and fractions in another, I’m leaving this the way it is.

Adding to Last Week’s Project

The rest of this is for those working through last weeks lesson. If you didn’t, you can skip this. If you worked through last week’s post and are wondering how to use this in that code, copy the double(fraction fractionString:String) and timeInterval(_ timeString:String) into the ViewController class of that project:

func double(fraction fractionString:String)->Double!{
        let separators = CharacterSet(charactersIn: " /")
        let components = fractionString.components(separatedBy: separators)
        print (components)
        var wholeNumber:Double = 0.0
        if components.count <= 3 && components.count > 0 {
            guard let number = Double(components[0]) else{
                return nil //invalid whole number
            }
            wholeNumber = number
        } else {
            return nil // wrong number of components
        }
        if components.count == 3{
            guard  let numerator = Double(components[1]) else {return wholeNumber}
            guard  let denominator = Double(components[2]) else {return wholeNumber}
            if denominator != 0{
                return wholeNumber + (numerator/denominator)
            } else {return wholeNumber} //division by zero will result in zero
        }
        return wholeNumber
    }

    func timeInterval(_ timeString:String)->TimeInterval!{
        let timeMultipiler = [1.0,60.0,3600.0] //seconds for unit
        var time = 0.0
        var timeComponents = timeString.components(separatedBy: ":")
        if timeComponents.count > timeMultipiler.count{
            return nil
        }
        timeComponents.reverse()
        for index in 0..<timeComponents.count{
            guard let timeComponent = Double(timeComponents[index]) else { return nil}
            time += timeComponent * timeMultipiler[index]
        }
        return time
    }

In the pickerView:didSelectRow: delegate, change the display to the label to this:

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        resultString = ""
        for index in 0..<components.count{ let digit = components[index][pickerView.selectedRow(inComponent: index)] if digit.characters.count > 1 {//add space if more than one character
                resultString += " " //add space if more than one character
            }
            resultString += digit
        }
        
//--- New Code for displaying doubles ----
        // display results as a string and as a double
        var value:Double! = 0.0
        if segmentedControl.selectedSegmentIndex == 2{
            value = timeInterval(resultString) //time
        }else{
            value = double(fraction: resultString) //fraction or decimal
        }
        displayLabel.text = "\(resultString) is \(value)"

    }

I got sneaky here. I only needed two conversion functions and not three because of that extra added feature of double(fraction:). Time calls the timeInterval function, everything else will call  the double(fraction:) function. Instead of unwrapping the value I used string interpolation to present the value. The resulting string will tell me this is an optional value. For a real app you’ll be doing some more unwrapping of course.

Build and run. For a decimal value you get this:
2016-12-20_08-03-05

For a time you get this:
2016-12-20_08-02-48

Unfortunately, for a string of 3 for the fraction you get this

2016-12-20_08-07-58

But it does work in this case.
2016-12-20_08-09-11

This is a problem with the picker. In the numberPickerComponent function the first element of the x case, "0" is a simple number character with no delimiter, and the string does not get broken:

case "x":
   return ["0","1/16","1/8","3/16",
           "1/4","5/16","3/8","7/16",
          "1/2","9/16","5/8","11/16",
           "3/4","13/16","7/8","15/16"]

Changing "0" to " 0" by adding a space in front is a cheap and easy way to solve that problem.

case x:            
return [" 0","1/16","1/8","3/16",
        "1/4","5/16","3/8","7/16",
        "1/2","9/16","5/8","11/16",
        "3/4","13/16","7/8","15/16"]

Build and run. Now it works.

2016-12-20_08-14-41

 

The process for converting any string into a double is the same. Find the characters that separate components. divide the string, check that the components are valid numbers, then add them to for your final result. As I’ve shown here, that might be different for the format you are using, but the general principles are the same.

The Whole Code

 

Here’s the week’s lesson as a Swift playground, formatted for the iPad playgrounds. Copy and paste into a playground on Xcode or iPad Playgrounds.   You can download and unzip the file here as well: numberstringparser-playground

import UIKit
//: A playground for converting Strings to Doubles (TimeInterval)

/*: #Case 1: A String that looks like number
 Double converts to an optional, where `nil` is the unconvertable value. */

let numberString = "3.1415926"
if let number = Double(numberString){
    print(number)
}

/*: # Case 2: A String that looks like a *mm:ss.ss* time
  - Break into components, using `components(separatedBy:)`
  - Unwrap each component, and add together
  - If anything goes wrong, return `nil` */
func minutesSecondsInterval(_ timeString:String)->TimeInterval!{
    var time = timeString.components(separatedBy: ":")
    if let seconds = Double(time[1]){
        if let minutes = Double(time[0]){
            return (seconds + (minutes * 60.0))
        }
    }
    return nil
}
//: Try this out
minutesSecondsInterval("10:13.6")

/*: # Case 3: A Flexible TimeInterval converter.
 - This has a constant array `timeMultiplier` holding a mutiplier for the number of seconds for the component
 - The function reverses the array with the `reverse()` method so components are alwys in the same position.
 - The function uses a loop to access the correct component and multiply by `timeMultiplier` before adding together.
 */
func timeInterval(_ timeString:String)->TimeInterval!{
    let timeMultipiler = [1.0,60.0,3600.0] //seconds for unit
    var time = 0.0
    var timeComponents = timeString.components(separatedBy: ":")
    if timeComponents.count > timeMultipiler.count{
        return nil
    }
    timeComponents.reverse()
    for index in 0..<timeComponents.count{ guard let timeComponent = Double(timeComponents[index]) else { return nil} time += timeComponent * timeMultipiler[index] } return time } //: Try it out: timeInterval("1:10:13.6") /*: Case 4: Fractions using a slash. - Fractions are strings like **33 1/3** or **w n/h** - Formula is `wholeNumber + (numerator/denominator)` - There are two separators, a space and a slash. Use the `components(separatedBy: separators)` function for a character set, creating a CharaterSet of the separators by `CharacterSet(charactersIn:)` */ func double(fractionString:String)->Double!{
    let separators = CharacterSet(charactersIn: "_/")
    let components = fractionString.components(separatedBy: separators)
       if components.count == 3{
        if let wholeNumber = Double(components[0]){
            if let numerator = Double(components[1]){
                if let denominator = Double(components[2]){
                    return wholeNumber + (numerator/denominator)
                } else {return wholeNumber}//no or incomplete fraction
            } else {return wholeNumber} //no or incomplete fraction.
        }
    }
    return nil //failure case
}
//: Try it out:
double(fractionString: "12 0/0")


/*: # Case 5: A better fraction converter
 - Deals with the bug of a fraction of zero case 3 doesn't.
 - Returns the whole number part if numerator or denominator invalid value. Nil for invalid whole number.
 - All values are doubles so *22.5 10.2/2.5* will return a correct decimal value of 26.58
 */
func double(fraction fractionString:String)->Double!{
    let separators = CharacterSet(charactersIn: " /")
    let components = fractionString.components(separatedBy: separators)
    var wholeNumber:Double = 0.0
    if components.count <= 3 && components.count > 0 {
        guard let number = Double(components[0]) else{
            return nil //invalid whole number
        }
        wholeNumber = number
    } else {
        return nil // wrong number of components
    }
    if components.count == 3{
        guard  let numerator = Double(components[1]) else {return wholeNumber}
        guard  let denominator = Double(components[2]) else {return wholeNumber}
        if denominator != 0{
            return wholeNumber + (numerator/denominator)
        } else {return wholeNumber} //division by zero will result in zero
    }
    return wholeNumber
}
double(fraction: "33 1/3")
double(fraction: "33 0/3")
double(fraction: "33 1/0")
double(fraction: "33")
double(fraction: "33.1234")
double(fraction: "33.1234 1.1/1.1")

Basic Tap, Pinch and Rotate Gestures

Many controls such as table views, map views, scroll views and buttons use gestures. You as a developer might want to use a gesture for your own purposes outside of these controls. The gesture recognizer classes can do that. The standard ones such a as tap, pinch and rotate are rather easy to set up, either by storyboard or by code. In this tutorial, I’ll show you how to set them up in code.

Set Up the Project

Make a new  single view project called TapPinchDemo in Swift with a Universal device.  Go to the storyboard. Add a label to the upper left of the storyboard. I used auto layout to pin the label 10up, 10 left and 10 right. Change the label’s font to Title.

Open the assistant editor.  Control drag from the label to the code and make an outlet named statusLabel.  Close the assistant editor and go to the ViewController.swift code. Add a constant at the beginning of the ViewController class for a color.

 let background = UIColor(red: 1.0, green: 0.98, blue: 0.96, alpha: 1.0)

In viewDidLoad add this color as the background.

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = background
}

Tap Gestures

There are three parts to a gesture: Configuring a gesture recognizer object, making an action for the object, and adding it to the view.  For the first gesture, you’ll make a tap gesture.  In viewDidLoad add the following line to make a tap gesture:

 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapAction(sender:)))

There’s two parameters in the constructor. Both indicate the location of an action that will be called when the gesture occurs. the first give the class, and the second the selector. selectors are functions called by the parameters of another function. While really a Objective-C thing, Swift had a compiler tag #selector() to indicate a selector. In the tap gesture tapAction(selector:) is the function called.

You’ll notice an error. Once you use a #selector, you must implement the function.  Add the function tapAction
above viewDidLoad

func tapAction(sender:UITapGestureRecognizer){
}

We’ll come back in a minute to finish filling out this code. Go back to viewDidLoad and configure tapGesture.

tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1

This sets the gesture recognizer to one tap with one finger. Add the gesture recognizer to the view using the addGestureRecognizer method.

view.addGestureRecognizer(tapGesture)

Go back up to the function tapAction. Gestures have states. You must check the state before you do anything. Specific states are necessary for specific gestures to work. For a tap, the state must be .ended. Add this code to the tap action selector:

if sender.state == .ended{
    statusLabel.text = "Tapped"
    view.backgroundColor = background
}

Build and run. Tap the screen and the label reads tapped.

2016-11-28_05-51-41

Pinch Gesture

The pinch gesture is a movement of two fingers moving towards or away from eacth other. Pinch gestures have no properties you need to set. Adding them is even easier than a tap. Just call the UIPinchGestureRecognizer constructor and add the gesture recognizer to the view. Add this to viewDidLoad.

 let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinchAction(sender:)))
        view.addGestureRecognizer(pinchGesture)

The action specified is a bit more complex than the tap. You have three states you might want to look at: .began, .changed, and .ended. Most often your concern is the changed state which occurs when a user moves his or her fingers. But you might find uses for beginning and ending a pinch. Add the following method to the ViewController code.

func pinchAction(sender:UIPinchGestureRecognizer){
        if sender.state == .began{
            statusLabel.text = "Pinch Began"
            view.backgroundColor = UIColor.yellow
        }
        if sender.state == .changed{
            statusLabel.text = String(format:"Pinch scale: %1.3f",sender.scale)
        }
        if sender.state == .ended{
            statusLabel.text = "Pinch Ended"
            view.backgroundColor = background
        }
    }

Pinches are examples of continuous gestures. These are gestures which will have more than one state to watch for. This changes the background when you have started the pinch and restores when you finish the pinch. When you change the value, it shows the scale value for the gesture. Scale is a value that shows the change between the user’s first pinch and the movement of the user’s fingers. Scale is the value you use in your applications. Build and run. Try pinching the screen. In the simulator, hold down the option key and drag on the trackpad or mouse.

Rotation Gesture

The last gesture we’ll discuss is rotation. Rotations set up just like pinches, except they use a UIRotationGestureRecognizer constructor. Add this to viewDidLoad:

let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotateAction(sender:)))
view.addGestureRecognizer(rotateGesture)

And add this function for the action. Like pinch, rotate is a continuous gesture, so I’ll set it up similar to  pinchAction.

func rotateAction(sender:UIRotationGestureRecognizer){
        if sender.state == .began{
            statusLabel.text = "Rotate Began"
            view.backgroundColor = UIColor.cyan
        }
        if sender.state == .changed{
            statusLabel.text = String(format:"rotation: %1.3f",sender.rotation)
        }
        if sender.state == .ended{
            statusLabel.text = "Rotate Ended"
            view.backgroundColor = background
        }
    }

The code will turn the background cyan when the user starts a rotate action. The display will give the rotation angle in radians using the property of a rotator gesture rotation. When the rotation ends with  the user removing fingers, the background returns to the default background color. Build and run. To simulate a rotation click down on the simulator using the mouse and then press Option(it’s annoyingly tricky). Move the cursor and you will have a rotation gesture.

This is only the beginning to using gestures, but should give you a good foundation for other gestures. While I most often use code to add gestures, you can also add then by dragging them onto the storyboard. Once added, you find them in the document outline, where you can control drag them to the code like any other object and make an action. Gestures my also conflict with each other, so try to keep the number in a single view to a minimum.

The Whole Code

//
//  ViewController.swift
//  tapSwipeDemo
//
//  Created by Steven Lipton on 11/25/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var statusLabel: UILabel!
    let background = UIColor(red: 1.0, green: 0.98, blue: 0.96, alpha: 1.0)
    func tapAction(sender:UITapGestureRecognizer){
        
        if sender.state == .ended{
            statusLabel.text = "Tapped"
            view.backgroundColor = background
        }
    }
    func pinchAction(sender:UIPinchGestureRecognizer){
        if sender.state == .began{
            statusLabel.text = "Pinch Began"
            view.backgroundColor = UIColor.yellow
        }
        if sender.state == .changed{
            statusLabel.text = String(format:"Pinch scale: %1.3f",sender.scale)
        }
        if sender.state == .ended{
            statusLabel.text = "Pinch Ended"
            view.backgroundColor = background
        }
    }
    
    func rotateAction(sender:UIRotationGestureRecognizer){
        if sender.state == .began{
            statusLabel.text = "Rotate Began"
            view.backgroundColor = UIColor.cyan
        }
        if sender.state == .changed{
            statusLabel.text = String(format:"rotation: %1.3f",sender.rotation)
        }
        if sender.state == .ended{
            statusLabel.text = "Rotate Ended"
            view.backgroundColor = background
        }
    }

    func swipeAction(sender:UISwipeGestureRecognizer){
        if sender.state == .began{
            statusLabel.text = "Swipe!"
        }
    }
   
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = background
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapAction(sender:)))
        tapGesture.numberOfTapsRequired = 1
        tapGesture.numberOfTouchesRequired = 1
        view.addGestureRecognizer(tapGesture)
        
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinchAction(sender:)))
        view.addGestureRecognizer(pinchGesture)
        
        let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotateAction(sender:)))
        view.addGestureRecognizer(rotateGesture)
        
    }
    

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


}

Custom Table View Cells in Swift 3

2016-10-14_06-53-47In iOS you are limited to two labels and an image for one of the standard table view cell styles. You might want more custom cells than this. You are not alone with  other developers including, Instagram, Twitter, Feedly and even Apple often using more than the basic table view cell. You can customize table view cells and populate them with any number of views in any size. In this lesson, I’ll show you how to make a custom table view cell to display four labels. We’ll add some logic to the custom view to conditionally show two labels for specials and give a 10% discount.

I’ll modify the app I created in the Table Views in Subviews Lesson. You can go to that lesson to learn more or download the starter file pizzatable_customcell_start.zip.

Creating a Custom Cell

Go to the storyboard. In the document outline, select the cell

2016-10-14_07-13-44

In the attributes inspector, Change the style to Custom.

2016-10-14_07-16-24

The  height of the custom cell will be bigger than the standard 44 point height. Click the ruler to change from the attributes inspector to the size inspector.  You’ll find the row height at the top of the inspector. Click on Custom and change from the default 44 points to 75 points.

2016-10-14_07-20-29

Click back to the attributes inspector. Drag four labels into the table view cell. Change the labels to Menu Item DescRegular Price $99.99, Special, and $99.99

 2016-10-14_07-24-48

Drag the labels to the four corners of the cell. Use the blue guides to align them to the corners.

2016-10-14_07-28-28

Select the Menu Item Desc Label. In the attributes inspector, change the font to a custom font of Georgia 24 point. Set AutoShrink to Minimum Font Scale of 0.5. In case the description is too long, this will shrink the font to fit.

2016-10-14_07-30-46

Select Special. Change the font color to Red(#ff0000) and the Font to System Heavy 17.0. Right align the label. Select the $99.99   label. right align the label and set the font to Gerogia 22 point.

2016-10-14_07-37-36

I’ll use  auto layout to lay things out cleanly.  Select Menu Item Desc. Control-drag up and to the left until the content view highlights.

2016-10-14_07-38-58

Release the mouse button. A menu appears.  Shift select Leading Space to Container Margin, Top Space to Container Margin and Equal Widths.

2016-10-14_07-40-55

This pins the label to the upper right side, and sets the width of the label to the width of the cell. I want 70% of the cell to be the label. With the label selected, Click on the width constraint, which should have some number like -198 in it

2016-10-14_07-45-31

In the attributes inspector, change the multiplier to 0.7

2016-10-14_07-48-18

Select the Regular Price $99.99 label. Control-drag from this label down and to the left until the content view highlights. Release the mouse button.  Shift select Leading Space to Container Margin, Bottom Space to Container Margin, and Equal Widths.

2016-10-14_07-50-38

Click Add constraints.  Select the width constraint

2016-10-14_07-52-19

Change the width of this label to 60% of the cell. In the attributes inspector, change the Multiplier to 0.6.

2016-10-14_07-56-51

Select Special, then Control-drag Up and to the right. Shift-Select Trailing Space to Container margin and Top Space to Container margin. Click Add Constraints

2016-10-14_07-58-10

Select Special, then Control-drag up and to the right. When you release the mouse button, select Trailing Space to Container margin and Bottom Space to Container Margin.  Click Add Constraints.

2016-10-14_07-58-46

Click the resolverresolver button. In All views in Table View Cell, select Update Frames.

2016-10-14_08-00-13

The cell’s format looks like this, with each label constrained to the corner.

2016-10-14_08-01-04

Connecting Up the Cell

Table view cells need their own class for outlets and actions.  Press Command-N to create a new Cocoa Touch Class Named CustomTableViewCell, subclassing UITableViewCell. Create the file. Once created, return to the storyboard. Select the cell in the document outline.  In the Identity inspector, change the class of the cell to CustomTableViewCell.

2016-10-14_09-33-57

Hide the right ad left inspector panes to give ourself some room for the assistant editor.  Click the assistant editor. You see code for the table view controller. To get to the cell’s controller class is slightly tricky. Xcode does not yet know it is there. You have to manually select it.  In the Assistant editor,  click where it says Automatic. In the menu tree that appears select Manual>PizzaTable>PizzaTable>CustomTableViewCell.swift

2016-10-14_09-39-05

 Make  four outlets for the labels. Control drag from the Menu Item Desc to the code to make an outlet menuItemDescLabel. Control-drag from the Special label to make an outlet specialLabel. Control Drag from Regular price $99.99 to make an outlet regPriceLabel. Control drag the $99.99 label to make the outlet priceLabel. Close the assistant editor.

Functions For the Cell

Open the left inspector and in the navigator go to the CustomTableViewCell.swift file. In the class just under the outlets,  add two constants for color and a discount amount for specials.

let lightGreen = UIColor(red: 0.5, green: 1.0, blue: 0.5, alpha: 1.0)
let lightRed = UIColor(red: 1.0, green: 0.9, blue: 0.9, alpha: 1.0)
let discount = 0.1

The cell can do much of its own setup and formatting, leaving the table view controller a lot cleaner and free of code. Add a function to convert the price to a string in the cell, which also concatenates a string in front of the price.

func string(_ prefix:String, for price:Double) -> String{
    let priceString = String(format: "%2.2f", price)
    return prefix + priceString
}

When an item is on special, it has a 10% discount. I’ll show the discounted price, the regular price and print the word Special on the cell if it is a special. To attract even more attention, I’ll make the background light green.  If not a special, I’ll make the background light red, and show just the price.  Add all that as a function.

func show(isSpecial:Bool,for price:Double){
    if !isSpecial{ //normal
        regPriceLabel.text = ""
        specialLabel.text = ""
        priceLabel.text = string("$", for: price)
        contentView.backgroundColor = lightRed    
    } else { //special discount
        regPriceLabel.text = string("Regular price $", for: price)
        specialLabel.text = "Special"
        priceLabel.text = string("$", for: price * (1.0 - discount))
        contentView.backgroundColor = lightGreen
    }
}

Using the Cell

You’ve set up the custom cell and added a controller to it. Change the Table view controller to use the cell.  Go over the TableViewController.Swift file. Find the tableView(tableview:CellForRowAt index path) method. Comment it out or delete it. I’ll start this from scratch. Add the method in again

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       
}

For  the first line create a cell by dequeuing a reusable cell as you have for any other table view. However, downcast the cell to CustomTableViewCell. Add the code to return the cell

let cell = tableView.dequeueReusableCell(
     withIdentifier: "cell",
     for: indexPath) as! CustomTableViewCell
        
return cell

the rest of the code goes above the return. Add a constant for the row from the index path.

let row = indexPath.row

The model has three arrays for price of a menu item, names of menu items and a bool for specials.  To add the menu item name, just assign it to the outlet menuItemDescLabel's text property.

cell.menuItemDescLabel.text = menuItems.names[row]

That’s all you need for many controls: just passing values to its outlets. You  can use a function from the cell controller.  For the price,  there is the function show(isSpecial:,for price:) in the cell. I call that and it decides how to display the other three labels.

cell.show(
    isSpecial: menuItems.specials[row],
    for: menuItems.prices[row]
)

The final method is this:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
   let row = indexPath.row
   cell.menuItemDescLabel.text = menuItems.names[row]
   cell.show(isSpecial: menuItems.specials[row], for: menuItems.prices[row])
   return cell
}

Configuring the Cell Height

Once you change the cell height, you must tell the table view the row height using the rowHeight property.  In viewDidLoad add the following.

 tableView.rowHeight = 75

This is the same number from the storyboard. I went simple here and just took the 75 points from the storyboard. There are more sophisticated ways to find the height, but I often go this simple route.

Running the Application

Build and run,

2016-10-14_11-53-43

Usually you’ll see two labels with the description and price in each cell. For a special, the cell changes to include the extra two labels, with the discount reflected. If you select an item, the functionality almost works. For a regular item the numbers are correct.

2016-10-14_11-55-37

However a special gives the regular, not the discounted price.

2016-10-14_11-55-57

Stop the simulator.  Change the tableview(tableview:didSelectRowAtIndexPath:) method to this:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let discount = 0.1
    let row = indexPath.row
    let name = menuItems.names[row]
    var price = menuItems.prices[row]
    if menuItems.specials[row]{price *= (1.0 - discount)} //adjust for discount
    delegate.didSelectItem(name: name , price: price)
}

This changes the price if the item is on special. Usually the price is the one given, if not, multiply off the discount. Try running again and the special discounts work.

2016-10-14_12-02-16

I kept the example to a few labels. What you can do in a table view cell is almost everything you can do in a view. You could add more advanced views, animations,  media players, and more as outlets. You can add buttons and switches to the cell, and set actions in CustomTableViewCell to change the cell or the app.

The Whole Code

You can download the completed project here: pizzatable_customcel.zip Below you will find a complete listing of the code.

CustomTableViewCell.swift

//
//  CustomTableViewCell.swift
//  PizzaTable
//
//  Created by Steven Lipton on 10/14/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class CustomTableViewCell: UITableViewCell {

    @IBOutlet weak var menuItemDescLabel: UILabel!
    @IBOutlet weak var specialLabel: UILabel!
    @IBOutlet weak var regPriceLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
    
    let lightGreen = UIColor(red: 0.5, green: 1.0, blue: 0.5, alpha: 1.0)
    let lightRed = UIColor(red: 1.0, green: 0.9, blue: 0.9, alpha: 1.0)
    let discount = 0.1
    
    func string(_ prefix:String, for price:Double) -> String{
        let priceString = String(format: "%2.2f", price)
        return prefix + priceString
    }
    
    func show(isSpecial:Bool,for price:Double){
        if !isSpecial{
            regPriceLabel.text = ""
            specialLabel.text = ""
            priceLabel.text = string("$", for: price)
            contentView.backgroundColor = lightRed
            
        } else {
            regPriceLabel.text = string("Regular price $", for: price)
            specialLabel.text = "Special"
            priceLabel.text = string("$", for: price * (1.0 - discount))
            contentView.backgroundColor = lightGreen
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

TableViewController.swift

//
//  TableViewController.swift
//  PizzaTable
//
//  Created by Steven Lipton on 10/2/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

protocol TableViewControllerDelegate{
    func didSelectItem(name:String,price:Double)
}

class TableViewController: UITableViewController {
    var menuItems = MenuItems()
    var delegate:TableViewControllerDelegate! = nil
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return menuItems.names.count
    }
    
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
        let row = indexPath.row
        cell.menuItemDescLabel.text = menuItems.names[row]
        cell.show(isSpecial: menuItems.specials[row], for: menuItems.prices[row])
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let discount = 0.1
        let row = indexPath.row
        let name = menuItems.names[row]
        var price = menuItems.prices[row]
        if menuItems.specials[row]{price *= (1.0 - discount)} //adjust for discount
        delegate.didSelectItem(name: name , price: price)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rowHeight = 75

      }
}

ViewController.swift

//
//  ViewController.swift
//  PizzaTable
//
//  Created by Steven Lipton on 10/6/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit



class ViewController: UIViewController,TableViewControllerDelegate {

    @IBOutlet weak var pizzaOrderedLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
    
    func didSelectItem(name: String, price: Double) {
        pizzaOrderedLabel.text = name
        let priceText = String(format:"Price: %02.2f", price)
        priceLabel.text = priceText
    }
    

    // MARK: - Navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "table"{
            let vc = segue.destination as! TableViewController
            vc.delegate = self
        }
    }


}

MenuItems.swift

//
//  MenuItems.swift
//  SwiftTableViewDemo
//
//  Created by Steven Lipton on 10/1/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class MenuItems:NSObject{
    let names:[String] = [
        "Margherita Pizza","BBQ Chicken Pizza",
        "Pepperoni Pizza","Sausage Pizza",
        "Seafood Pizza","Sausage Deep Dish",
        "Meat Lover's Deep Dish","Veggie Lover's Deep Dish",
        "BBQ Chicken Deep Dish","Mushroom Deep Dish",
        "Tiramisu","Vanilla Ice Cream",
        "Apple Crostata","Hot Fudge Pizza",
        "Soft Drink","Coffee",
        "Espresso","Mineral Water"]
    let prices:[Double] = [
        7.95,11.49,
        8.45,8.45,
        12.75,10.65,
        12.35,10.00,
        16.60,11.25,
        6.50,2.25,6.50,
        9.75,1.25,
        1.25,3.50,3.75
    ]
    let specials:[Bool] = [
        false,true,
        false,false,
        false,false,
        true,false,
        false,true,
        false,false,
        false,true,
        false,false,
        true,false]
}

What Do you do with Any?

There’s been a major change to Swift 3.0 That many people might find a little disturbing in code, and others may have no idea what it is. In one sense that’s the idea. Apple changed AnyObject to Any in Swift 3.0. This three-letter word is an extremely powerful feature,a backbone of many functions and classes, and the core of watchOS’s way of transmitting data from one controller to another.

The class Any is the object that is anything. It is generic. You learned early in your coding lessons that everything has a type and everything gets assigned a type like this:

let radius = 5.0   // a Double
var diameter:Int = 10  //an Int
var pizzaTopping = "Pepperoni"  //a string

Functions need a type in their parameters too. For example, there is this function

func pizzaArea(radius:Double)-> Double {
let area = radius * radius * M_PI
return area
}

Since everything needs a type to work right, a function assigning something like this might seem a little odd.

func pizzaSize(context:Any) -> Double {
    var size = 0.0
    let radius = context as! Double
    size = radius * radius * M_PI
    return size
}

If you tried

print(pizzaSize(context: 5.0))

You would get a result around 78.54.

Any really isnt totally generic. It’s a place holder because there are times when writing code, you have to be vague in how you add a parameter. Look closely at pizzaSize(context:Any). You’ll see this is identical to pizzaArea(radius:Double) but I set the type in my code instead of in the parameter. To set the type, I downcast the type Any to Double using as!. Another example

let myAnyType:Any = 12.0
let myDoubleType:Double = myAnyType as! Double
print(myDoubleType)

Why would you want to delay typing? Because you, or some other developer such as Apple wrote some code and has no idea how you are going to use that code. Consider the following class

class PizzaGeometry{
init(context:Any){
self.awake(context: context)
}
func awake(context:Any){}
}

This code by itself does nothing but call an empty function awake(context:) and have an empty function pizzaArea. But we could subclass this to do areas of pizzas for both integers and doubles by awake(context:).

class PizzaAreaInt:PizzaGeometry{
    var radius:Int = 0
    override func awake(context: Any) {
        radius = context as! Int
    }
    func pizzaArea() {
        print (radius * radius * 3)
    }
}

class PizzaAreaDouble:PizzaGeometry{
    var radius:Double = 0.0
    override func awake(context:Any){
        radius = context as! Double
    }
    func pizzaArea(){
        print(M_PI * radius * radius )
    }
}

I couldn’t have done that had I set the type in PizzaGeometery. Most of you are probably iOS developers and haven’t touched watchOS yet. However in Apple’s obsession about keeping things simple in watchOS, PizzaGeometery is the skeletal definition of a view controller , named WKInterfaceController class in watchOS. watchOS passes data between view controllers directly without, like iOS,  referencing the destination in prepare(forSegue:) or initializing the destination class and setting properties. It sends those values as a context, and lets the awake(withContext context:any) deal with it. Developers unpack the context for the controller they are currently writing by overriding awake. However Any is not just a watchOS thing. iOS uses it too, and one form of this shows up all over the place: Dictionaries

Area for circles is a single property. What if we wanted to make a class for more than one property in PizzaGeometery?  That’s where dictionaries come in. In Swift, you can define a dictionary of type [String:Any].  What does that do? It means you can have an identifier name as a key and place its value — no matter the type — as a the value. for example:

let rectanglePizzaDictionary:[String:Any] = 
    ["width":10,"length":12,"height":1.2]

I can unpack a dictionary like that in my Subclass

class PizzaRectangle:PizzaGeometry{
    var height:Double = 0.0
    var width:Int = 0
    var length:Int = 0
    override func awake(context: Any) {
        let contextDictionary = context as! [String:Any]
        height = contextDictionary["height"] as! Double
        width = contextDictionary["width"] as! Int
        length = contextDictionary["length"] as! Int
    }
    func pizzaArea(){
        print(width * length)
    }
    func pizzaVolume(){
        print (width * length * Int(height))
    }
}

I unpacked twice. First I unpacked to the type [String:Any]. Then I unpacked the entries in the dictionary.  Here’s the one issue you’ll need to be careful with here. For simplicity I didn’t error check, but if any of these values are not what I expect for the dictionary the app will crash. You need error checking here and maybe even more importantly, good documentation of the possible dictionary entries. As iOS developers this is going to be a big problem since a lot of methods in UIKit use this dictionary format to send information back to you as a developer. Apple used to be worse at this, but their current documentation system seems to have solved a lot of this. For example one of the excruciating examples of this is the UIImagePickerControllerDelegate method  imagePickerController(_:didFinishPickingMediaWithInfo:). There’s now a link in the documentation, but for a while it was a very hard search to find the keys and value in the info dictionary.

Any is very helpful. Apple uses it often once you start looking for it. Any means “I don’t know what supposed to go in here, could you set it up for me” when it comes to values. Dictionaries of [String:Any] exist all over the API, often in delegates and closures. watchOS makes it the way to pass values between interface controllers. What at first glance looks like an empty generic container is really a powerful way of deferring type until necessary, making for a more flexible programming environment.

Why do we need Delegates in iOS and WatchOS?

About two years ago someone asked me a very good question: Why do we need delegates for UIViewControllers?  He thought Swift  made things easier, but this delegate stuff seems very complicated. Shouldn’t we be able to send a message or initializer between classes?

When I first learned iOS, I’ll admit it took me months to understand what happened with delegation. I found lots of confusing code and little explanation. AS I was researching some more preferences to send people to, I found the results lacking. Most often, tutorials refer to how to use an Apple factory delegate, not making your own callback. Its these callbacks which require full knowledge of delegates.

I decided it was time to update this, and to include two examples developers might run into: the iOS and watchOS versions. With the maturing of watchOS in watchOS 3 I think more developers might begin to look at developing watch apps, and there’s some twists there that might cause some confusion.

Let’s start at the beginning, so everyone understands the problem.

What is a Class?

Let’s start at the beginning, so everyone understands the problem. While we use classes in object-oriented programming, it’s good to review what they actually are. A class is a collection of data, which we call properties, and actions we can do to those properties, which we call methods.

Properties and methods are either public or private. A public method is one that classes other than the defining class can see and can use. Private means that the property or method is only usable and visible within the defining class. Other classes cannot see or use it. In Swift the private keyword makes properties and methods private. Swift’s calculated properties feature is another way to make properties private. In Swift, there is also a default state which makes a method or class public to the current target, but not other targets.

We tend not to like other people messing with our insides, and that is a good programming practice too. In general, it is best to leave what is public by other classes to a necessary minimum. Keeping properties and methods private and not exposing all of our class is known as encapsulation.

Encapsulation allows us to make code as modular as building blocks. Just as a few stubs come out of an otherwise standard sized brick, only a few usable methods come out of a class. Then they can attach to a lot of other bricks.

What is  Model –  View – Controller  or MVC?

MVC schematic blankA term heard often  when working with Xcode, is MVC. MVC stands for Model-View-Controller. It is not an exclusive term to Xcode projects. It is a pattern of programming, a good organization of any program or application in a graphics-rich environment, and arguably any environment that interacts with the user. MVC separates the major parts of an application. First it separates the data and the user interaction then adds an intermediary between them. Why is this important? You might write and publish an application for an iPhone, then decide an iPad version would be a good idea, then decide to make a watch version. With MVC, you only change one part completely, the view and possibly some of the controller. The code handling your data never changes between the versions saving a lot of time and effort.

What is a Model?

MVC schematic modelThere are parts of our program that deal with information we want to process. A pizza ordering system has a list of data giving us information about each person’s order. There may be other links to that data with more data about each customer, and about each pizza. In a pizza ordering system this is our model: the collection of all the data we will use in our ordering system. It does not in any way interact with the user. It does not display anything or does it ask for input. It is just data. Here is an example of very simple model:

class switchState{
    var state:Bool
    func textState()->String{
        if state {
            return "On"
        } 
        return "Off"
    }
    func overLoadSwitch(users:Int,load:Int){
      let totalLoad = users * load 
      if totalLoad > 100{
          switch = false
      }
}

This model is the state of a switch.  That’s the data. the model has two methods. textState(), describes the state of the switch as a string,  overLoadSwitch() turns off the switch if users multiplied by load is greater than 100 . There is a lot more methods I should add to describe the switch, but any method is changing or describing data only. There is no user input or output here. Models might make calculations but again there is no user interaction here.

What is a View?

MVC schematic viewWhere all the user interaction happens is in the view. In Xcode, most people use Interface Builder either as a scene in a storyboard or a .xib file to build their views.  A developer can programmatically create a view class to hold the different controls.

2016-08-01_07-11-48As the model never interacts with the user,  the view never interacts directly with the data. The view doesn’t do much but sit there. It might respond to a user touch with feedback such as a notifying a method somewhere, a color change when a button gets tapped or a scrolling motion at times, but that is all it does. The view does contain a lot of properties and methods and that tell us the state of the view. We can change the appearance and behavior of the view through methods and properties. The view can tell the controller that there was a change in the view, such as a button getting pressed, or a character typed. it can’t do anything about it, but it can broadcast something.

What is a Controller?

MVC schematicThe heart of MVC connects these two. Called the controller or view controller, it coordinates what happens in the model and what happens in the view. If a user presses a button on the view, the controller responds to that event. If that response means sending messages to the model, the view controller does that. If the response requires getting information from the model, the controller does that too. in Xcode, @IBOutlet and @IBAction connect Interface Builder files containing views to the view controller.

The key to MVC is communication. To be more accurate, the lack of communication. MVC takes encapsulation very seriously.  The view and the model never directly talk to each other. The controller can send messages to the view and the controller. The view and controller may do an internal action to the message sent as a method call or it may return a value to the controller. The controller never directly changes anything in either the view or the model.

So to summarize, a view, a model and a controller cannot directly change a property in each other. A view and a model may not talk to each other at all. A view can tell the controller there is a change in the model. A controller can send messages in the form of method calls to the view and the model and get the responses back through that method.

MVC schematic full annotated

How Does MVC Connect to Other MVC’s

What we’ve discussed so far is for only one scene in a much larger application. Suppose I have the following  watchOS storyboard:

2016-09-21_05-01-58

I have an a button Switch which loads a second face that has a switch.  When I decide which way I want the switch, I press done.

The Easy Direction

In Xcode we have what are known as segues. Segues are a convenience to point from one view controller to another.  When segues keep track of some things that would become cumbersome to control ourselves in a simple way. When we move from one controller to the next, the segue tells the system to open this particular view controller, which then opens up a view and model. The model and view in the new MVC setup is different then the calling one. Apple includes a method prepare(for segue:) for  iOS which give us a chance to set values in the new view controller,  and subsequently the new view controller’s view and model.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "switch"{
         let vc = segue.destination as! SwitchViewController
         vc.switchState = false
        }

With the introduction of WatchOS came a slightly different approach.  instead of having the full model available,  watchOS sends to new controllers a single value called context. As this is type Any?,  you can put whatever you want in this value. Most often developers send dictionaries of values to other controllers through the contextForSegue method. When the destination application wakes up. the awake method  converts the context to the proper type and assigns it correctly.

override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
        return self
    }

For both a identical  phone or watch app I could press the Switch button.  It launches  the switch face and  pass a value to the switch to be false.

The Problem Direction

We can turn the switch off and on easily enough.  But when we press Done to send it back to the original controller  is when problems show up. By the rules of MVC, we need a method to return a value. Where in a called instance can we go back to the class calling it? With an encapsulated class we can’t. There is no way to send that revised model back to the original controller without breaking encapsulation or MVC. The new view controller does not know anything about the class that called it. We look stuck. If we try to make a reference directly to the calling controller, we may cause a reference loop that will kill our memory. Simply put, we can’t send things backwards.

This is the problem that delegates and protocols solve by being a little sneaky. Imagine another class, one that is really a skeleton of a class. This class contains only methods. It declares that certain methods are in this class, but never implements them. In Swift they are protocols. We make a protocol class that has one method. That method is what you do when you are done  with the switch, and want to go back to the calling controller. It has a few parameters, things you want to pass back to the calling view controller.  So it might look like this:

protocol SwitchDelegate {
    func didFinishSwitch(switchState:Bool)
}

I passed back the state of the switch in this case.

In the controller with the switch, we make an instance of this protocol, calling it delegate.

delegate:SwitchDelegate! = nil

Since we have a property of type SwitchDelegate, we can use the methods of the SwitchDelegate type, In our example, that is our method didFinishSwitch. We can stick that method call in a action for a Done button:

@IBAction func doneButtonPressed(sender:UIButton!){
    delegate.didFinishSwitch(switchState: switchState)
    dismiss(animated: true, completion: nil)
}

or for WatchOS

@IBAction func submitSwitchStatus() {
    delegate.didFinishSwitch(switchState: switchState)
    pop()      
}

Since protocols are skeletons, it means any other class can adopt them. A class makes the protocol methods part of its own class with a stipulation. As soon as a protocol gets adopted, you need to flesh out the skeleton. The developer has to code the required methods in the adopted class’ code. We adopt a protocol by placing it after the name of the class and superclass. For iOS you might have

class OrderPizzaViewController:UIViewController,PizzaEditDelegate

and for watchOS, you might have

class InterfaceController: WKInterfaceController,SwitchDelegate {

As soon as you do that, you will get a compiler error since the protocol’s method does not exist in the class. In the code for the adopting class, in our example OrderPizzaViewController, we would implement the method

func didFinishSwitch(switchState: Bool) {
        if switchState {
            textState = "Switch is On"
        } else {
            textState = "Switch is Off"
        }        
    }

We get the data back, and what we need with it, in this case a string that we’ll print to the label.
One more step. While back in the destination controller, I said the delegate was an instance of the protocol, I didn’t say where the delegate was. In prepare(for Segue) I add one more line vc.delegate = self saying the protocol is your controller

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "switch"{
         let vc = segue.destination as! SwitchViewController
         vc.switchState = false
         vc.delegate = self
        }

In WatchOS this gets a bit trickier. I have only one context to pass both the switchState and the delegate. For multiple values generally developers use a dictionary like this:

override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
        let context:[String:Any] = ["switchState":false,"delegate":self]
        return context
    }

the awake method would have code to unwrap the dictionary and assign the values.

When we tap Done in a watch or phone application, the method runs, it knows it is located in the calling controller, and calls it there where we added to the original class.The data is a parameter so the program can easily transfer into the controller and to the model. Delegates and protocols are bit sneaky but it works, and is one of the most important techniques when working with view controllers.

mvc-schematic

The original question asked why didn’t Swift make this simpler. As I hope I’ve shown here with a Swift context, it doesn’t matter what object oriented language you use. Delegates are part of the MVC pattern, which is a good programming practice.

Understand and Use Closures in Swift

Have you ever seen some factory method in UIKit and see this strange parameter called completion or completionHandler? Usually you put nil there and leave it alone. These strange things like (Double,Double) -> Bool things are closures. Closures are functions you use as types, often in parameters of other functions. In this lesson we’ll explore closures and get under the hood to explain what they are and what they are really doing.

Set Up a Playground

Set up a playground in Xcode, the iPad or on the web at the IBM Swift Sandbox. IF using the IBM Swift Sandbox, make sure you have Foundation imported.

import Foundation

The Basics of Closures

Suppose I want to calculate the volume of a deep dish pizza. Add these functions to the playground:

 //area of a round pizza
    func roundArea(radius:Double) -> Double{
        return M_PI * radius * radius
    }
    //volume of a round pizza
    func roundVolume(height:Double, radius:Double ) -> Double{
        return roundArea(radius: radius) * height
    }

Add a variable to compute the volume of a pizza of radius 6 and height 2, then print it

var volume = roundVolume(height:2,radius:6)
print( String(format:"Round volume %4.2f",volume))

Run this and get the volume from the area on the console. Which is fine if I had only round pizzas. What if I had rectangle, oval or triangle pizzas? I’d have to write two new functions each: one for the area and one for the volume. However in each case I’m only changing the area calculation. What if I could pass just the calculation to the volume function? That’s what a closure is: a way of writing something like a function but assigning it as a type. Add the function to the class.

func volume(
    height:Double, 
    dim1:Double,
    dim2:Double, 
    area:(Double,Double) -> Double) -> Double
    {
        return area(dim1,dim2) * height
    }

Look at the parameter area. I’ve declared it (Double,Double) -> Double. That looks a lot like a function that returns Double, and two Double parameters. That’s what a closure does as a type. A closure gives a skeletal structure like a function you can pass as a parameter. Below the volume function add the following line of code.

let rectangleArea = { (length:Double,width:Double) -> Double in
    return length * width
}

When I declare the contents of the identifier, I flesh out something like a function inside of curly braces. Instead of func before the parameters, I put in after the return type. There are parameters (length:Double,width:Double). I have the return type as a Double. For closures, there must always be a return value. If you don’t have one mark it Void or ().

Since I declared the type in the closure declaration, I often can remove it from the declaration

        
let  rightTriangleArea = {(length,width) -> Double in
    return length * width / 2.0
}  
let ovalArea = {(length,width)->Double in
    return length/2.0 * width/2.0 * M_PI
}

It doesn’t work in all cases though. This give an error if you do not specify the type

let roundSliceArea = { (radius:Double, slices:Double) ->Double in
    return M_PI * radius * radius / slices
}

I can use the same volume function and change how I compute the area.

print (volume(height: 2,dim1: 10, dim2: 12, area:rectangleArea))
print (volume(height: 2,dim1: 10,dim2: 12, area:roundSliceArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:rightTriangleArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:ovalArea))

I do have a problem with a circle. I could define it like this in my storyboard:

 let circleArea = {(radius)->Double in
            return radius * radius * M_PI
 }
print (self.volume(height: 2,dim1: 10, dim2: 12, area:circleArea))        

This doesn’t work for the circle, because the number of parameters are different. You’d get this error message
Cannot convert value of type '(Double) -> Double' to expected argument type '(Double, Double) -> Double'

You have choices: Use the oval with the same length and width or a placemarker.  I tend to choose the place marker. Change circleArea to add the extra parameter. Fix the above code to this:

let circleArea = {(radius:Double, placeMarker:Double) -> Double in
    return M_PI * radius * radius 
}
print (volume(height: 2,dim1: 10, dim2: 12, area:circleArea))

Literal Closure in Parameters

You don’t have to assign the closure to a constant. You can write it explictly in the function. For a rectangle area, you can write this in the playground.

 
volume = volume(
    height: 2,
    dim1: 10,
    dim2: 12,
    area: {(width,length)->Double in
        return width * length
    }
)
        print(volume)

You’ll find this a very common way of specifying the closure. When the closure is the trailing parameter, you can place it after the function like this:

volume =  volume(
    height: 2,
    dim1: 10,
    dim2: 12) 
    {(radius,placemarker)->Double in
        return M_PI * radius * radius
    }
print(volume)

Closures as Completion Handlers

One very common use of closures is completion handlers. Make a new function like this:

func volume(
    height:Double,
    dim1:Double,
    dim2:Double,
    completionHandler:(Double)->()
){
    let result = dim1 * dim2 * height
    completionHandler(result) 
}

Neither the completionHandler closure nor the volume function returns anything. Instead the result is the parameter of the completion handler.
Why would anyone do this? The reason is asynchronous processing. There are methods which Apple or the developer don’t want to run in the main thread. If you are querying a database, or opening a file any results appear in a closure. The database query might take some time, and you don’t want the rest of your application to freeze while you wait for the result. Similarly, while loading a file you don’t want to freeze the system waiting to learn if the load was successful.
Instead of return in these functions, the method runs on another thread at its own pace and lets the main thread go on its merry way. When done, the method calls something like completionHandler, handler, or completion with the results it returns as the completion parameter.

The developer uses the result their implementation of the completion handler. This is why most often closures happen completely within a function call. Usually they are specific to the code around it. For example, the volume function with the completionHandler would code like this to save the result to the volume property and output the result.

volume(height: 2,dim1: 10,dim2: 12)
{(result)->() in
    print(result)
    volume = result  //in a class use self
    resultLabel.text = String(format:"Completion %5.2f")
}

Completion handlers often pass off the value to a property in a class, like the code above assigns the value of  result to volume. If you run the code above within a class, be sure to include the class identifier or self. volume = result should be self.volume = result. The identifiers within a closure have no scope to the class. They must be explicitly stated.

A Real Example of a Completion Handler

As a general rule, you’ll find completion handlers when you don’t know when you will complete a task. One good example of this is presenting alert views.

Set up a new single view project named ClosureDemo, with Swift as the language. Go to the storyboard and drag a label and button.

Set the font on both to Title 1.  Select the button. Title the button Alert. Click the alignment iconalignment icon in the auto layout menu bar. Check Horizontally in Container and Vertically in container.  Change Update from to Items of new constraints like this:

center alignment

Click Add 2 constraints.  Select the Label. Title it Results.  Click the pin icon pinMenuButton in the auto layout toolbar.  In the dialog box that appears type 10 then tab, 10 then tab and 10 then tab. Click down to the bottom and Update frames with Items of New Constraints.

2016-09-02_07-07-01

Click Add 3 constraints.

Open the assistant editor and control-drag the label to the code. Name the outlet resultsLabel. Control-Drag from the button to the code. Change to an action. Name the action presentAlert.

Close the Assistant editor.  Open the Viewcontroller.swift code.

In the presentAlert action, add the following code:

 @IBAction func presentAlert(_ sender: UIButton) {
     volume = 240.0
 //example of a real completion handler.
     let alert = UIAlertController(
         title: "Alert",
         message: "Volume is \(volume)",
         preferredStyle: .alert)
   

Alert actions only activate after the alert shows and the action’s button gets tapped by the user. The UIAlertAction initializer uses handlers to describe the action when pressed. Add this to the compute action:

   
let clearAction = UIAlertAction(
    title: "Clear", style: .destructive,
    handler: {(action)->() in
        self.volume = 0
        self.resultLabel.text = "Cleared"
    }
)
 

In clearAction, the user sets the volume property to 0 and reflects that in the Label.  Again, the closure is independent of the scope of its class. You must specify self in order to use the ViewController classes’ properties and methods.

Add two more examples of actions:

       
let doubleAction = UIAlertAction(
    title: "Double",
    style: .default,
    handler: {(action)->() in
        self.volume *= 2
        self.resultLabel.text = "\(self.volume)"
    })
let cancelAction = UIAlertAction(
    title: "Cancel",
    style: .cancel,
    handler:{(action)->() in
       self.resultLabel.text = "\(self.volume)"
    })
   

Add the actions to the alert

alert.addAction(clearAction)
alert.addAction(doubleAction)
alert.addAction(cancelAction)

Finally present the alert controller. present has a completion handler. Add a print statement to it to see it in action, then a print after the present.

 
present(alert, animated: true, completion: 
{()->Void in
    print ("present Completed")
 })
 print ("Outside Handler")

Build and run. Tap the Alert button. You get the alert. The console printed this:

Outside Handler
present Completed

After calling present, The system executed the next step, and did not wait for it to finish printing  Outside Handler. Once the presentation completed, the system printed present Completed. The alert shows:

2016-09-12_06-03-57

Tap the double button. The label reads 240. Try the other buttons.

2016-09-12_06-04-38

You’ll find closures in most often in these handlers. Anything that takes and unpredictable amount of time will use a handler in a closure.

The Whole Code

You can find the code for this tutorial on the IBM Swift Sandbox. For cutting and pasting into an Apple Playground on iPad or Xcode, Use the code below.

//
// Closure Demo file 
// For MakeAppPie.com  closure tutorial 
// Sep 2016 by Steven Lipton 
// 
//
import Foundation

 //area of a round pizza
    func roundArea(radius:Double) -> Double{
        return M_PI * radius * radius
    }
    //volume of a round pizza
    func roundVolume(height:Double, radius:Double ) -> Double{
        return roundArea(radius: radius) * height
    }
    var volume = roundVolume(height:2,radius:6)
print( String(format:"Round volume %4.2f",volume))

//Closure to change the area formula
func volume(
    height:Double, 
    dim1:Double,
    dim2:Double, 
    area:(Double,Double) -> Double) -> Double
    {
        return area(dim1,dim2) * height
    }
    
    //Assigning type (Double,Double) -> Double to Classes
    
    let rectangleArea = { (length:Double,width:Double) -> Double in
    return length * width
}
let  rightTriangleArea = {(length,width) -> Double in
    return length * width / 2.0
}  
let ovalArea = {(length,width)->Double in
    return length/2.0 * width/2.0 * M_PI
}
let roundSliceArea = { (radius:Double, slices:Double) ->Double in
    return M_PI * radius * radius / slices
}
//Trying out the volume function

print (volume(height: 2,dim1: 10, dim2: 12, area:rectangleArea))
print (volume(height: 2,dim1: 10,dim2: 12, area:roundSliceArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:rightTriangleArea))
print (volume(height: 2,dim1: 10, dim2: 12, area:ovalArea))

//Fitting a single parameter formula with a placemarker
let circleArea = {(radius:Double, placeMarker:Double) -> Double in return M_PI * radius * radius }
print (volume(height: 2,dim1: 10, dim2: 12, area:circleArea))
// closure within parameters
        volume = volume(height: 2, dim1: 10, dim2: 12, area: {(width,length)->Double in return width * length})
        print(volume)
// Trailing closure outside parentheses
volume =  volume(height: 2, dim1: 10, dim2: 12) {
     (radius,placemarker)->Double in
          return M_PI * radius * radius
}

//Closures as Completion Handlers
func volume(
    height:Double,dim1:Double,dim2:Double,
    completionHandler:(Double)->()
){
    let result = dim1 * dim2 * height
    completionHandler(result) 
}

// For asynchronous processing, use the closure instead of returning the result. 
volume(height: 2,dim1: 10,dim2: 12)
{(result)->() in
         print(result)
         volume = result
         
}

The ClosureDemo Alert project

This is the code for the ClosureDemo Alert project. Add the label and button as described above than connect to this code in ViewController.

//
//  ViewController.swift
//  ClosurePlay
//
//  Created by Steven Lipton on 9/9/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    var volume = 0.0
    
    @IBOutlet weak var resultLabel: UILabel!
    @IBAction func compute(_ sender: UIButton) {
        volume = 240.0
        //example of a real completion handler.
        let alert = UIAlertController(
            title: "Alert",
            message: "Volume is \(volume)",
            preferredStyle: .alert)
        
        let clearAction = UIAlertAction(
            title: "Clear", style: .destructive,
            handler: {(action)->() in
                self.volume = 0
                self.resultLabel.text = "\(self.volume)"
        })
        
        let doubleAction = UIAlertAction(
            title: "Double",
            style: .default,
            handler: {(action)->() in
                self.volume *= 2
                self.resultLabel.text = "\(self.volume)"
        })
        let cancelAction = UIAlertAction(
            title: "Cancel",
            style: .cancel,
            handler:{(action)->() in
                self.resultLabel.text = "\(self.volume)"
        })
        
        
        alert.addAction(clearAction)
        alert.addAction(doubleAction)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: {()->Void in
            print ("In present Handler")
        })
        print ("Outside Handler")
        
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    
}

How to Make a Tab Bar Controller in Swift 3.0 Code

While Navigation controllers often have the limelight when it comes to Xcode’s view  controllers, tab bar controllers are better for independent tasks in the same app, or for different ways of working with the same model. In this lesson we’ll take a look at  how to make them in Swift programmatically. If you are interested in tab bar controllers on the storyboard, You might want to read this post. For passing data between tabs read here.

Setting Up

While there are very easy storyboard ways of making tab bar controllers, we can do much of this programmatically. Start with a single view template and create a Swift project called TabProgDemo.  Once the project loads, on the keyboard hit Command-N. Create a  Cocoa Touch Class named PizzaViewController, subclassing of UIViewController.Make the language Swift, and check the mark to make a Xib file for an iPhone like this:

2016-07-12_10-38-48

Press Command-N again and Make another Cocoa Touch Class like the first, subclassing UIViewController. Name it PieViewController again  creating a XIB file.

2016-07-13_06-44-57

I have created a file of image assets for this lesson. You can get them here as a zip file  or you can right click and save  the images below:

Pie Iconpizza icon

pizza_bar_icon@2xpizza_bar_iconpie_bar_icon@2xpie_bar_icon

Save these images on your drive. Open up your Assets.xcassets folder in Xcode. Select all the files in finder and drag them into the assets folder.

2016-07-13_06-15-30

Go to the the PieViewController.xib file. Make sure in the lower left of Interface builder it reads  the default setting:

view as 6s

 If, not set it to iPhone 6s by clicking it and selecting the device.  Set the background color to Light Green(#AAFFAA). Drag out a label and title it Pie at 32 points. Add the color Pie Icon. Your XIB should look like this:

2016-07-13_06-46-52

Select the PizzaViewController.xib file. Set up the Xib the same way as the pie controller. Use a Light Red(#FFAAAA) for the background:

2016-07-13_06-30-30

Tab Bar Programmatically

Click open the AppDelegate.swift file. While much of this can be important stuff, we’ll clear it out so we can concentrate on tab bar controllers. Replace the class with the following:

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    func application(application: UIApplication,
                      didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        return true
    }
}

Let’s put this code together part by part. First add this under the function declaration:

let tabBarController = UITabBarController()

Creates an instance of a UITabBarController. This will be the controller we’ll configure in this function. We are doing this here so it is global to the application, one of the few times you want to do something globally.

Create the two view controllers from the XIB files. In full accordance with the adage “Life is uncertain, eat dessert first”, we’ll make the first page on launch be the pie page.

let tabViewController1 = PieViewController(
    nibName: "PieViewController",
    bundle: nil)
let tabViewController2 = PizzaViewController(
    nibName:"PizzaViewController",
    bundle: nil)

Add these three lines:

let controllers = [tabViewController1,tabViewController2]
tabBarController.viewControllers = controllers
window?.rootViewController = tabBarController

UITabBarController has a property called viewControllers, which is an array of the view controllers in the order displayed in the tab bar. Our first line of this segment makes an array of view controllers in the order we want them to present. Next we assign the array to the tab bar controller. The last line assigns the controller as the root view controller.

We have our controller set up, but no titles or icons. This code assigns the title and image.

tabViewController1.tabBarItem = UITabBarItem(
    title: "Pie",
    image: UIImage(named: "pie_bar_icon"),
    tag: 1)
 tabViewController2.tabBarItem = UITabBarItem(
    title: "Pizza",
    image:UIImage(named: "pizza_bar_icon") ,
    tag:2)

Each view controller has a property tabBarItem which contains the icon and text for the tab bar. We use the UITabBarItem(title:String,image:UIImage,tag:Int) initializer to create the tab bar items. In our example,  we set the first one to Pie and use the pie_bar_icon image  for the first. The second uses Pizza and the pizza_bar_icon  image.

Set your simulator to the iPhone 6s. Build and run. You get the Pie first:

 2016-07-14_05-35-42

You’ll notice the tabs the bottom. Tap the pizza tab.

2016-07-14_05-36-10

 

You have working tabs.