Category Archives: Swift Swift

Tab Bar Controllers in Xcode 8 Storyboards

In this lesson, we’ll take a look at tab bar controllers and how to add them in the storyboard.  For more on implementing them completely in code see Swift Swift: Using Tab Bar Controllers in Swift. For more on data sharing once set up in the storyboard, see Passing Data in Tab Bar controllers

The Real Difference between Navigation and Tab Bar Controllers

Before we begin, It’s important to remember the difference between  tab bar and navigation controllers. Navigation controllers are stack based, while tab controllers are parallel in execution. Think of a navigation controller as a stack of cards. You can only play the visible card. If you put a card on top of another card, only the top card is active.

2016-07-06_06-20-22

Now imagine you take several cards and deal them face up. You can play any face up card. They all are active. This is a tab bar controller.

2016-07-06_06-19-45

As we go through lessons on tab bar controllers, it is very important to remember this difference.

Creating From the Template

The easiest way to make a tab bar controller is through Xcode’s template. In Xcode, press Command-Shift-N or File>New>Project. Select the tabbed application.
This will give you a window with two tabs installed with two labels each and a tab item on the bottom of each controller. The template tends to make things more complicated instead of less, so it is rarely used.

Creating From A Single View

The most common way to make a tab bar controller is to start from a single view and embed it in a controller. We’ll try an example of this one in Xcode. Press Command-Shift-N and select Single View. Make a file named SwiftTabBarEmbed, using Swift as the language and with a Universal device. Once created, go over to the Storyboard.  Make sure the Preview setting is  View as: iPhone6s(wC hR) in the lower left corner of the storyboard:

2016-07-05_06-18-15

Change the color of the view controller’s background.

2016-07-05_06-08-41

Select the view controller by clicking the view controller icon or title, and select Editor>Embed in>Tab Bar Controller.

2016-07-05_06-06-10

This will turn the single view controller into the first view controller of the tab bar.

2016-07-05_06-12-22

Add another view controller by dragging out a view controller then control-dragging from the tab bar controller to the new controller. In the pop-up, select under Relationship Segue the view controllers option.

2016-07-05_06-14-57

Another segue appears on the story board.

2016-07-05_06-16-22

You’ll see a second tab in the tab bar controller.

2016-07-05_06-21-14

Adding More Tabs with System Icons

To configure the tab bar icon, go to the Item icon in the  Yellow View Controller (not on the tab view controller) we just made and click it.

2016-07-05_06-23-33

This will bring up in the properties inspector the Tab Bar Item and Bar Item properties. In the Tab Bar Item properties is the System Item drop down. This is where we configure the tab bar button.

2016-07-05_06-25-14

In the System Item menu, Click the drop-down for System Item. you will see a  list of system icons.

2016-07-05_06-26-37

Select Favorites. The Icon changes both on the view controller and the tab bar controller.

2016-07-05_06-28-51

2016-07-05_06-28-26

Click the other view controller’s tab bar, and change the system Icon to Recents.

2016-07-05_06-31-06

2016-07-05_06-31-27

Using  the More Feature

Click on the Preview size Button which reads iPhone 6s(wC hR). A new toolbar appears underneath it. Select the iPad 9.7″.

2016-07-05_06-36-26

The view controllers in the storyboard change to iPads, so you may need to change the zoom level.

2016-07-05_06-40-45

Drag out  six more view controllers, so we have a total of eight controllers.  Change their background colors to make then easily distinguishable.

2016-07-05_06-47-05

Control-drag from the tab bar controller to each new view controller and assign ContactsBookmarksSearch, Downloads, and Most Viewed to the controllers, leaving one more as a custom item. The tab bar controller should look like this:

2016-07-05_06-52-27

Change the Preview mode back to to an iPhone 6s. While your icons may be in a  different order, you will get something like this for the tab bar at the bottom of the phone:

2016-07-05_06-56-32

Compact widths, the width on most phones, cannot handle more than five tabs. If you add more than five tabs, it places a More tab as the fifth tab. What does the More tab do? Build and run as an iPhone 6s in the simulator.

2016-07-05_07-04-24

Tap on the More icon in the lower right. A table appears with the missing tabs.

2016-07-05_07-07-21

 More gives you access to all your tabs. If you tap the tab in the table’s cell, the tab’s controller will appear. Select Downloads, and you get the downloads tab.

2016-07-05_07-10-13

Tabs selected by More act slightly differently than ones in a regular tab. They are embedded in a navigation controller. You’ll see at the top a navigation bar.

2016-07-05_07-14-57

The navigation bar goes back to the More table.  Tap Bookmarks, and notice the navigation bar is not there, then tap More again and the Downloads controller is still there.

   2016-07-05_07-18-21    2016-07-05_07-10-13

Select More  in the navigation bar to go back to the Table view

2016-07-05_07-07-21

User Editing the Tab Bar with More

Without any extra code, tab bar controllers let users customize their tab bars. Tap the Edit button, and you get a configuration view:

2016-07-05_07-23-00

Looking closer, you’ll see that the hidden tabs are highlighted and the visible tabs are dimmed

2016-07-05_07-23-20

Drag the Item tab down to the tab bar. Drop it on top  of Recents. It replaces Recents.

2016-07-05_07-27-25

You can also drag tabs in the tab bar to change the order. Drag Downloads on top of Contacts. Drag  Downloads to the right, swapping it with Bookmarks.  Your tab bar now looks like this:

2016-07-05_07-37-20

I want to add two cautions to this: One of the system icons you can use is a More icon. Be careful when using the More icon in a tab bar. It can cause user confusion between the More tab and your own use for More. Make sure it is clear what you are doing.

The second caution is more insidious. When programming for tab controllers, you can get the tabs from an array. However, the order and visible tabs can change by user request with  the More tab. Avoid hardwiring the tabs by their location in the array since users can change them.

Close the simulator. In the storyboard, delete the Bookmarks, Recents and Favorites view controllers (or their segues if you don’t mind the warnings)  so you get a tab bar like this:

2016-07-05_07-45-54

As we now have five icons, this will keep off the More icon, which requires six tabs to show up. You can reorder the tabs in the tab view controller by drag and drop.   Reorder using drag and drop to this:

2016-07-05_07-50-02

Custom Tab Bar Items

Click on the tab bar for the Item view  controller. We can see all the tab bar properties.

2016-07-05_06-25-14

The lower half has bar item properties, which directly control the title and icon for the tab. We can easily change the text. Change the Bar Item Title to Pie.

2016-07-05_07-55-00

The title changes on the tab bar:

2016-07-05_07-55-36

If we change a System items’s title, it becomes a custom item, and the icon disappears. Do the same to change Downloads to Pizza. Also change Contact to Square.  SquarePizza and Pie have squares for icons.

2016-07-05_08-00-30

Icon Images

Under Title in the properties inspector,  we have a drop down for Image.

2016-07-05_07-55-00

For a custom bar item we can supply the icon. Image icons are not like other images. They are monochrome images,  unlike anything else you have ever seen. They are set not by color but by the alpha value , or transparency of the image. You can make any image here any color, but it will show up as a solid tint color if you select an alpha value of 100% and background color if you select 0% alpha. For example I created the following icons in both high and low resolutions:

gradient bar Pizza and Pie Bat items pizza bar icon pizza bar icon@2x pie bar icon pie bar icon@2

Icons are 30 points by 30 points, which means for retina images make the icon a 60 pixel by 60 pixel image and for low resolution images 30 pixel by 30 pixel. You can download them individually by right clicking the images above, and saving them to a folder.  Use these file names:

Screenshot 2015-01-27 07.37.51

You can also download a.zip file Of these here:Tab Bar Assets

For those not familiar with Apple image naming conventions, low resolution images have a file name. Retina Images have @2x appended to that file name. This tells Xcode to know what resolution it is. We have both 60×60 pixel retina and 30×30 pixel standard images. By loading both into Xcode, the system will use the correct one automatically for the device.
In Xcode’s navigator panel, open up Assets.xcassets. Select all the icon files. Drag them into the center panel in Xcode, which will highlight. Release the mouse button, and you will see a new collection in the image assets.

2016-07-05_08-06-31

Return to the storyboard. On the tab we titled Square,click anywhere on the tab bar.  Select the image drop-down in the tab bar properties and select the gradient bar icon.

2016-07-05_08-09-01

The Gradient bar now replaces the place marker  square:

2016-07-05_08-11-16

Find the Pizza and Pie tab views on the storyboard, and change them the same way. The Tab bar controller now has three custom icons.

2016-07-05_08-12-21

Build and run with the iPhone 6 simulator, and you will see our three icons, with  Pie  highlighted as selected.

Using View Controllers in Tab Bar Controllers

As mentioned earlier, tab bar controllers make a set of single view controllers running parallel to each other. The tab bar switches the view, but the view we leave does not get dismissed, just hidden. there are two uses for such a controller. The first is a series of small independent applications related to each other. Apple’s clock app is an exmaple. The timer, alarm clock, world clock, and stopwatch all are related by a theme of time. All run independently of each other. The second use of Tab bar controllers is to use a shrared data set in different ways. Social media apps like Twitter or Instagram for example use tab bars to have different functions of displaying posts, searches, or displaying notifications that all use the same database. For this second type of use, see see Passing Data in Tab Bar controllers. We’ll look at the first type here.

Basic View Controllers

Connecting a view controller in tab bars is the same as hooking up any other view controller. Make a new view controller class by pressing Command-N. Make a new Cocoa Touch Class view controller named SearchViewController. Change the class code to this.

class SearchViewController: UIViewController {
    var count = 0
    @IBOutlet weak var myLabel: UILabel!
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        myLabel.text = "Count \(count)"
        count += 1
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        count = 1
    }
}

Note how I used viewDidLoad and viewWillAppear here. In tab bar controllers, ViewDidLoad only fires once, when we first load the view controller. Use it for first initializations only. The code above initializes the count to 1. The method viewWillAppear fires every time the view appears, and so we use that to do any updating. This code assumes that this view controller will remain active even when it is not visible.

Go to the storyboard and select the Search view controller on your storyboard. In the identity inspector set the class to SearchViewController.

2016-07-06_05-43-22

On the search view controller  in the storyboard drag a label to the center of the view.

2016-07-06_05-34-14

Set the label font size to 32 points. Click the auto layout align button alignment icon  at the bottom right of the storyboard. In the menu that appears, check Horizontally in Container and Vertically in Container  with values of 0 points. Set Update Frame to Items of New Constraints.

2016-07-06_05-36-05

Add the two constraints. Open up the assistant editor. Drag from the circle next to the outlet to the label to connect it.

2016-07-06_05-49-06

Build and run. Select the search tab, then another tab a few times. You will see the count increase every time you return to the search tab.

2016-07-06_05-51-50     2016-07-05_08-15-11     2016-07-06_05-52-13

Embedding Navigation Controllers in Tab Bar Controllers

You can also embed navigation controllers into tab controllers. Stop the simulator and go back to the storyboard. Close the Assistant editor if it is open. Select the Pizza View Controller’s icon on the storyboard. In the drop down menu, select Editor>Embed in>Navigation Controller

2016-07-06_05-58-46

The view controller changes into a Navigation controller with the tab bar icon. Attached to the navigation controller is a view controller.

2016-07-06_06-05-43

 On the view controller, Double click the center of the navigation controller bar. Change the title to Pizza!!!. Drag a bar button item to the upper left of the controller on the storyboard. Title it More Pizza.

2016-07-06_06-08-10

Add another view controller, next to  the one we just configured. Control-Drag from the bar button item to the new view controller. Set up a show segue. In the new view controller, Drag a Navigation item to the navigation bar. Change the item’s title to More Pizza!!!

2016-07-06_06-10-09

Build and run. Select the Pizza tab and we have our navigation controller. Tap the More Pizza Button. The More Pizza!!! View Appears. Now go to another tab and then back to the Pizza tab. We remain on the More Pizza!!! view.

2016-07-06_06-13-41

You can use navigation controllers inside a tab bar controller to your heart’s content. However, tab bar controllers can only be used once in a project, as the top level controller. Embedding more tab bar controllers does not work.

This is the basics of tab bar controllers. For more information on sharing between the controllers and a more extensive coding example, see Passing Data in Tab Bar controllers.

Using Attributed Strings in Swift 3.0

For most uses, a basic string works well. Normal strings have no character or paragraph formatting. They rely on the properties of the control to format them. The system only formats the full string, not parts of it. For formatting a part of a string, you need an attributed string which specifies parts of the string formatted in different ways. In this lesson we’ll learn how to work in attributed strings in Swift.

Set Up A Project

For this project, you can use a playground on Xcode or on iPad or add the code as an Xcode project. Let’s look at the setup for each.

Starting  a Xcode App Project

In Xcode use the Command-Shift-N keyboard shortcut to open a new project Swift3AttributedString. Use an iOS Application of a Single View Template.

Go to the ViewController.Swift file, and clean out the ViewController class to look like this:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

    }

 

Starting a Xcode Playground

In Xcode,  Press Option-Command-Shift-N to get a new playground. Use an iOS playground named Swift3AttributedString.

2016-07-02_09-22-46

Starting a iPad Playground

Open the iPad application. If you have  playgrounds displayed, press the browser button on the upper right.

2016-07-02_09-07-26

Click either Add ( + ) button in the browser.

IMG_0388

If you select the one in the upper left, you’ll be asked if you want to load a file from an external source as well. Select Create playground

IMG_0389

For either Create Playground button, you’ll be asked for which template to use. Select Blank

IMG_0390

Code Setup for Playgrounds

Set up your playground code like this:

import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

    }
//Uncomment these lines if using Playgrounds 
let vc = ViewController()
PlaygroundPage.current.liveView = vc
PlaygroundPage.current.needsIndefiniteExecution = true

We’ve created the same empty ViewController class as the app.  At first, you might see Xcode complaining about a syntax error: just ignore it for now. For the iPad version, you’ll see the contents in an automatically opening window. If you are in Xcode, open the assistant editor set to Timeline.

Setting Up the View Controller

For this lesson, we only need one label, so to be compatible on all devices we’ll set it up programmatically with a quick and dirty way.  Go over to the ViewController class. Change viewDidLoad to the following to add a label:

override func viewDidLoad() {
   super.viewDidLoad()
   // declarations     
   let myLabel = UILabel()
   let myString = "P is for Pizza and Pizza is for me"
   //set the view background color     
   view.backgroundColor = UIColor.white()        
   //set up the label
   myLabel.frame = view.bounds.insetBy(dx: 20, dy: 20)
   //myLabel.frame.size.width = 350.0 //Uncomment for playgrounds
   myLabel.lineBreakMode = .byWordWrapping
   myLabel.numberOfLines = 0 //Variable number of lines
   myLabel.backgroundColor = UIColor.yellow()
   myLabel.text = myString
   view.addSubview(myLabel)

Build and run. You’ll get  something like this:

2016-07-03_15-19-39

Here we used the text property of UILabel, giving us our plain old label.  We left a margin of white background  for some thing that will happen later. Let’s now jazz this up.

Making a Mutable String

We will need an  attributed text string. There are two varieties NSMutableAttributedString and AttributedString. If you are wondering why one has a NS in front and the other doesn’t so am I. Since I’m using Beta 1 of Xcode 8, Apple is still working on removing all the NS‘s they promised to remove. If your code doesn’t work because it can’t find something with an NS prefix, try removing the NS.

I’m using a NSMutableAttributedString so I can add attributes as we go along in the lesson. If you set attributes only once, you can do that with AttributedString. We’ll look at one later.

In the viewDidLoad method after the label setup, add the following code:

let myMutableString = NSMutableAttributedString(
    string: myString,
    attributes: [NSFontAttributeName:UIFont(
        name: "Georgia",
        size: 18.0)!])
//Add more attributes here

//Apply to the label
myLabel.attributedText = myMutableString

Line 1 initializes an attributed string with the string myString and an attribute dictionary. Here we used an attribute named NSFontAttributeName as the key, and a UIFont object as the value. Build and run using the default iPhone SE . You will see this in the label:

2016-07-01_06-16-43

An Attributed Text Sampler

Once you make the mutable attributed string you can change it as much as you want with the addAttribute method. There are a lot of attributes to change. For a list of them, you can look here in Apple’s documentation. I’ll go through a few useful or fun ones for examples.

Changing Fonts

Add the following below the Add attributes here comment:

myMutableString.addAttribute(NSFontAttributeName,
    value: UIFont(
        name: "Chalkduster",
        size: 24.0)!,
    range: NSRange(
        location: 9,
        length: 5))

We use the NSFontAttributeName attribute as our attribute and 24 point Chalkduster as our font.  That is the same as the initializer. What is new is range. An NSRange is a struct used for sequential data, such as strings. It has a location and a length. In the case above the location is 9 and the length 5. If you do the counting, this is the first Pizza in our original string. This will change the font of Pizza to 24 point Chalkduster. Build and run

2016-07-01_06-21-10

Changing Text Color

Add these attributes under the last one:

myMutableString.addAttribute(NSFontAttributeName,
    //: Make a big blue P
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "AmericanTypewriter-Bold",
                size: 36.0)!,
            range: NSRange(
                location:0,
                length:1))
        myMutableString.addAttribute(
            NSForegroundColorAttributeName,
            value: UIColor.blue(),
            range: NSRange(
                location:0,
                length:1))

This time we changed two attributes. First we set the the font of the P to 20pt American Typewriter bold. We also charged the color the the P to blue with the NSForegroundColorAttributeName  attribute. Build and run:

2016-07-03_12-42-58

Making Outline Fonts

Add these three attributes under those:

//: Make the second pizza red and outlined in Helvetica Neue
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Helvetica Neue",
                size: 36.0)!,
            range: NSRange(
                location: 19,
                length: 5))
        myMutableString.addAttribute(
            NSStrokeColorAttributeName,
            value: UIColor.red(),
            range:  NSRange(
                location: 19,
                length: 5))
        
        myMutableString.addAttribute(
            NSStrokeWidthAttributeName,
            value: 4,
            range: NSRange(
                location: 19,
                length: 5))

The first attribute we know already. We set 36pt Helvetica Neue to the second Pizza. Then NSStrokeColorAttributeName sets the color for the outline of Pizza to Red. The third attribute uses NSStrokeWidthAttributeName to set a stroke width of 4 points, giving us an outlined letter. Build and run this:

2016-07-03_12-51-01

Character Background Change

You can set the background as well, but be careful. Add this to the code under the last code:

//: Set the background color is attributes text. 
//: which is not the color of the background text.
let  stringLength = myString.characters.count
myMutableString.addAttribute(
    NSBackgroundColorAttributeName,
    value: UIColor.magenta(),
    range: NSRange(
        location: 0,
        length: stringLength))
        

Our range this time will be the complete string. The first line gets the length of the string using characters.count. The NSBackgroundColorAttributeName attribute will change the background for the characters to magenta, but it does not change the background for the label view. Our label is still yellow, but our font background is magenta. Build and run:

2016-07-03_13-07-13

The font background will be the height of the largest letter, and as long as the range specified. If the label is bigger than the text size, you will have two background colors.

Adding a Drop Shadow to Text

You can also add drop shadows to text. Before adding the drop shadow you need to define a drop shadow object

 //: Add a Drop Shadow
        
        //: Make the Drop Shadow
        let shadow = NSShadow()
        shadow.shadowOffset = CGSize(width: 5, height: 5)
        shadow.shadowBlurRadius = 5
        shadow.shadowColor = UIColor.gray()

We’ve set a offset of 5 points by 5 points, a blur radius of 5 and a gray shadow. Now we can use the NSShadowAttributeName to make a shadow, using shadow for the value, and change the font to 48 point Menlo.

        
        //: Add a drop shadow to the text
        myMutableString.addAttribute(
            NSShadowAttributeName,
            value: shadow,
            range: NSRange(
                location: 27,
                length: 7))
        
        //:Change to 48 point Menlo
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Menlo",
                size: 48.0)!,
            range: NSRange(
                location: 27,
                length: 7))

Build and run.

2016-07-03_16-05-12

Appending Text

Suppose you wanted to add three exclamation marks to the end of our sentence. That requires this code:

let myAddedString = AttributedString(string: "!!!", attributes: nil)
myMutableString.append(myAddedString)

Build and run:

2016-07-03_15-38-09

The attribute  argument above were set to nil. When using nil, the attributes are the initialized attributes. If we want to have different attributes, we define them as a dictionary of [String:AnyObject]?. The string would be the AttributeName, and the value the value: argument we used in the previous examples. For example, let’s make the exclamation point be AvenirNext-Heavy at 48 point with the drop shadow we defined earlier. Add this just above the myAddedString definition.

//: appending the String with !!! as an Attributed String
let myAddedStringAttributes:[String:AnyObject]? = [
    NSFontAttributeName:UIFont(
        name: "AvenirNext-Heavy",
        size: 48.0)!,
    NSForegroundColorAttributeName:UIColor.red(),
    NSShadowAttributeName: shadow
            
]

Change the nil to myAddedStringAttributes

let myAddedString = AttributedString(
    string: "!!!",
    attributes: myAddedStringAttributes)
myMutableString.append(myAddedString)

Build and run

2016-07-03_15-39-53

This is just the start of what you can do with attributed text.

The Whole Code

This code is compatible with both Projects and Playgrounds. For it to work with playgrounds, uncomment the lines instructing you to do so.


//  ViewController.swift
//  SwiftAttributedString
//
//  Created by Steven Lipton on 10/18/14.
//  Revised Swift 3.0 7/1/16
//  Copyright (c) 2014,2016 MakeAppPie.Com. All rights reserved.
//

import UIKit
//import PlaygroundSupport  //uncomment for Playgrounds
class ViewController: UIViewController {
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myLabel = UILabel()
        let myString = "P is for Pizza and Pizza is for me"
        
        view.backgroundColor = UIColor.white()
        //initialize the label to be the entire view
        //and to word wrap
        
        myLabel.frame = view.bounds.insetBy(dx: 20, dy: 20)
        //mylabel.frame.size.width = 350.0//uncomment for playgrounds
        myLabel.lineBreakMode = .byWordWrapping
        myLabel.numberOfLines = 0
        myLabel.backgroundColor = UIColor.yellow()
        myLabel.text = myString
        view.addSubview(myLabel)

        //: Initialize the mutable string
        let myMutableString = NSMutableAttributedString(
            string: myString,
            attributes: [NSFontAttributeName:
                UIFont(name: "Georgia", size: 18.0)!])
       
        //: Add more attributes here:
        //: Make the first Pizza in chalkduster 24 point
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Chalkduster",
                size: 24.0)!,
            range: NSRange(
                location: 9,
                length: 5))
        
        //: Make a big blue P
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "AmericanTypewriter-Bold",
                size: 36.0)!,
            range: NSRange(
                location:0,
                length:1))
        myMutableString.addAttribute(
            NSForegroundColorAttributeName,
            value: UIColor.blue(),
            range: NSRange(
                location:0,
                length:1))
        
        
        //: Make the second pizza red and outlined in Helvetica Neue
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Helvetica Neue",
                size: 36.0)!,
            range: NSRange(
                location: 19,
                length: 5))
        
        myMutableString.addAttribute(
            NSStrokeColorAttributeName,
            value: UIColor.red(),
            range:  NSRange(
                location: 19,
                length: 5))
        
        myMutableString.addAttribute(
            NSStrokeWidthAttributeName,
            value: 4,
            range: NSRange(
                location: 19,
                length: 5))
        
        //: Set the background color is attributes text. 
        //: which is not the color of the background text.
        let  stringLength = myString.characters.count
        myMutableString.addAttribute(NSBackgroundColorAttributeName,
                                     value: UIColor.magenta(),
                                     range: NSRange(
                                        location: 0,
                                        length: stringLength))
        
        
        
        //: Add a Drop Shadow
        
        //: Make the Drop Shadow
        let shadow = NSShadow()
        shadow.shadowOffset = CGSize(width: 5, height: 5)
        shadow.shadowBlurRadius = 5
        shadow.shadowColor = UIColor.gray()
        
        //: Add a drop shadow to the text
        myMutableString.addAttribute(
            NSShadowAttributeName,
            value: shadow,
            range: NSRange(
                location: 27,
                length: 7))
  
        //:Change to 48 point Menlo
        myMutableString.addAttribute(
            NSFontAttributeName,
            value: UIFont(
                name: "Menlo",
                size: 48.0)!,
            range: NSRange(
                location: 27,
                length: 7))
        
        //: Appending the String with !!! and an Attributed String
        let myAddedStringAttributes:[String:AnyObject]? = [
            NSFontAttributeName:UIFont(
                name: "AvenirNext-Heavy",
                size: 48.0)!,
            NSForegroundColorAttributeName:UIColor.red(),
            NSShadowAttributeName: shadow
            
        ]
        let myAddedString = AttributedString(
            string: "!!!",
            attributes: myAddedStringAttributes)
        myMutableString.append(myAddedString)

        //: Apply to the label
        myLabel.attributedText = myMutableString
       
    }
    
}

//Uncomment this for use in playgrounds.
/*
 let vc = ViewController()
 PlaygroundPage.current.liveView = vc
 PlaygroundPage.current.needsIndefiniteExecution = true
 */

How to Use UIImagePickerController for a Camera and Photo Library in Swift 3.0.

2016-06-28_08-20-54

Almost every iPhone and iPad now has a camera. Many people want to use a camera in their apps for various reasons. While you may not be building the next Instagram or Twitter, there are many places where photo documentation comes in handy.

There are two ways to use the camera. The more powerful and advanced method is using AVFoundation, but it is also a very complicated way to get a photo out of the camera.

The simpler way is the UIImagePicker. This is a quick way as Swift has simplified much of the code for it.  It is also the best way to fetch photos from the photo album. In this lesson, we’ll set up a basic camera and library app. In upcoming posts we’ll add a few embellishments to it that will make for a more effective camera.

Set Up The Layout

We’ll be talking about some of the issues with camera on both iPad and iPhone, so we’ll make this a universal app.  We will be using a dab of auto layout as well. If you want a brief introduction to auto layout go over and take a look at my book Practial Autolayout. You won’t need it for this lesson, but it may make what I do a little more understandable.

Make a new project SwiftPizzaCam with a single view and using Swift. As already mentioned, make the device Universal.

If you are not familiar with Xcode 8’s interface builder bar and the auto layout icons, here is a guide to help you as we go through setting up the app.

2016-06-28_05-29-33

Click on the iPhone 6s class preview. A new selection of previews appears.

2016-06-28_05-39-43

Select the iPad 9.7″ device.  then zoom out to 50%

2016-06-28_05-55-24

Xcode 8 come with a series of easily accessible preview modes in interface builder.  We will put a photo into an UIImageView when we first  use the image, and doing so on an iPad is easier than an iPhone, though it will work on both.  Right-click and save the image below:

pizza

Click on Assets.Xcassets and drag the pizza file into the assets folder. Go back to the story board and select the clip icon.  You will find the pizza media there.

2016-06-28_05-49-14

Drag out the pizza to the view and drop it into the view controller.

2016-06-28_05-58-42

Click the pin pinMenuButtonbutton.  Click off Constrain to Margins. Click on all the I-beams, then set their value to 0 points  like the image below:

2016-06-28_06-01-52

Be sure to press tab after you type in any number in this popover. It has an annoying habit of forgetting them if you don’t. Select Update Frames: Items of New Constraints towards the bottom.  Click  Add 4 Constraints. In the properties change the View Mode to AspectFit to properly size the photo.

Screenshot 2014-12-03 08.48.32

Now drag out a label to the storyboard. Set the background property to a Light Gray( #AAAAAA) color with a 65% alphaCenter Align the label text with a 28 point font size. Change the text of the label to read Pizza Cam!!!.  Select the label,  and then  click the pin button pinMenuButton. Set the top  8 points,  left and right sides 0 points, but not the bottom, like this:

2016-06-28_06-24-32

Make  sure you select Update Frames: Items of New Constraints towards the bottom before clicking  Add 3 Constraints.

Drag out  a toolbar and place toward the bottom of the view. Using the pin menu pinMenuButton, Pin it to the bottom, left and right like this, again updating the frames:

2016-06-28_06-21-16

Add a bar button item and a flexible space bar item to the toolbar. In one of the two bar buttons label it Photo and the other Library. Your tool bar and layout should look like this:

2016-06-28_06-26-34

 

Select the iPhone 6s in the preview modes. It should look like this:

2016-06-28_06-27-22

If you did everything correctly, you should have no auto layout warnings. If you do, go to the resolver and click Update Frames on the bottom half of the menu that appears.  If things get messier after doing this, clear the constraints on the bottom of the resolver, and pin everything again.

Wire Up the Outlets

Your next step is to wire up all the outlets and actions. Open the assistant editor.  Control-drag from the pizza photo  image and make an outlet called myImageView. Control drag from the Library button and make an action called photoFromLibrary with a sender of type UIBarButtonItem. This is important for stuff we will do later. Do this again, but with the Photo button and named shootPhoto. Again, make the sender UIBarButtonItem.
You can clean up the code a bit if you like. When done you should have something like this:

import UIKit

class ViewController: UIViewController{
    @IBOutlet weak var myImageView: UIImageView!

    @IBAction func shootPhoto(_ sender: UIBarButtonItem){
}
    @IBAction func photofromLibrary(_ sender: UIBarButtonItem) {
   }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    }
}

Add the UIImagePickerController and Delegate

The star of our show the UIImagePickerController is missing. We do that programmatically. Close the assistant editor, and open ViewController.swift. Add the following line under the outlet for myImageView:

let picker = UIImagePickerController()

The UIImagePicker does much of its works from a delegate. Add the following to the class description:

class ViewController: UIViewController, 
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate {

We actually have two delegates: UIImagePickerControllerDelegate and UINavigationControllerDelegate. The UINavigationControllerDelegate is required but we do nothing with it. In the viewDidLoad, add the following:

 override func viewDidLoad() {
        super.viewDidLoad()
        picker.delegate = self
    }

We have wired up the delegate. Unlike other delegates, there is no required methods. However, this will not work without implementing two methods. At the bottom of the class, add the following:

//MARK: - Delegates
func imagePickerController(_ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [String : AnyObject])
{
        
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        
}

These two methods handle our selections in the library and camera. We can either handle the cancel case with imagePickerControllerDidCancel or handle media with didFinishPickingMediaWithInfo

Getting a Photo from the Library

The UIImagePickerController is a view controller that gets presented modally. When we select or cancel the picker, it runs the delegate, where we handle the case and dismiss the modal. Let’s implement the photo library first, then the delegates. Add the following code to the photoFromLibrary method:

@IBAction func photoFromLibrary(_ sender: UIBarButtonItem) {
   picker.allowsEditing = false
   picker.sourceType = .photoLibrary
   picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
   present(picker, animated: true, completion: nil)
}

To get to the photo picker, it is three lines of code. We already initialized picker. In line two, we tell the picker we want a whole picture, not an edited version. In line three, we set the source type to the photo library. Line four we set the media types for all types in the photo library. Line five calls present to present the picker in a default full screen modal.

If you build and run, you could press the photoFromLibrary method, but then get stuck in the library. We need delegates to get out of the library. First let’s add the code for the imagePickerDidCancel delegate method:

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
       dismiss(animated: true, completion: nil)
    }

This does nothing special: it dismisses the modal controller. If we do pick a photo, we want to do something with the photo. Add the following code to the didFinishPickingMediaWithInfo delegate method:

func imagePickerController(_ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage //2
    myImageView.contentMode = .scaleAspectFit //3
    myImageView.image = chosenImage //4
    dismiss(animated:true, completion: nil) //5
}

One of the parameters of this method is info. It has a dictionary of various information about the selected media, including metadata,  a user edited image if the .allowsEditing property is true, and a NSURL location of the image. For our camera app, the important key is the UIImagePickerControllerOriginalImage. We take that key and put the image into a variable. We could make this a constant, but there is some future iterations of this project that will need this as a variable.

Camera images are usually bigger than a UIImageView on a device. To shrink them quickly to a visible size, the best way is to set the .contentView property of our UIImageView to .ScaleAspectFit as we do in line 3.

Security for the Photo Library

Run in the simulator as an iPhone 6s. You should get a screen like this:

2016-06-28_07-07-54

Tap the Library bar button. The app crashes:

2016-06-28_07-08-30

In the simulator, there’s no message about what happened, the system just ends the application. Apple requires any use of the camera, the photo library or any personal information for that matter asks the user to agree sharing information with the app on the first use. For the UIImagePicker, this is included in your first run of the app.  Starting with iOS10, there’s another layer of security on the photo library. The developer must make an entry in the info.plist describing why they want to use the photo. That description will show up in an alert when the user decides to allow access to the library.

There’s two ways to add this entry: in XML or in the property list. We’ll use the property list first.  In Xcode,  select Info.plist from the Navigator pane

2016-06-28_07-21-27

Right click on the Information Property List Row. In the menu that appears, select Add Row.

2016-06-28_07-22-27

You get a new row with a drop down menu. Select Privacy – Photo Library Usage… from the menu.

2016-06-28_07-28-07

With the row still highlighted, click the value column  and add why you want access to the photo library.  In this app we are setting a background image.

2016-06-28_07-31-16

If you wish to add this directly to XML, the key/value is this

<key>NSPhotoLibraryUsageDescription</key>
<string>Set the Background</string>

We’ll come back to this for the camera, and do it in XML.

Build and Run again. Click the Library button, and you will get the permissions alert with the description we placed in the property list:

2016-06-28_07-33-57

Tap OK.  We get our Image picker,

2016-06-28_07-41-08     2016-06-28_07-41-29

When we tap a photo, it appears on the app.

2016-06-28_08-00-41

Running as an iPhone and iPad app: Using Popovers

Stop the app and run as an iPad 2. After answering OK to the library privacy question you get this:

2016-06-28_08-04-30

Which looks fine, except Apple doesn’t like it. They want you to use a  popover. Popovers must access the photo library on iPads.   Fortunately, it is very easy to get the popover to work. Add the highlighted lines in photoFromLibrary

    @IBAction func photoFromLibrary(_ sender: UIBarButtonItem) {
        picker.allowsEditing = false
        picker.sourceType = .photoLibrary
        picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
        picker.modalPresentationStyle = .popover
        present(picker, animated: true, completion: nil)
        picker.popoverPresentationController?.barButtonItem = sender
    }  

Line 5 selects a presentation style of a popover. We then present the popover. Line 7 sets the reference point for the popover to the bar button item. As I mentioned in another post popovers need a reference rectangle to pop up from. In this case, we can use the UIBarButtonItem which happens to be sender . This is why it was so important to use UIBarButtonItem as the sender type.

Build and run with the iPad 2 simulator. Now we have a popover to pick a photo.

2016-06-28_08-14-44

 

Since present checks the class size, you will get the proper behavior on any phone .The iPhone 6s Plus in the simulator does this.

2016-06-28_08-20-16 2016-06-28_08-20-54

 

We save ourselves from hours of device fragmentation work this way.

Adding a Camera

Basic Camera code is almost the same as adding a photo library. Change the shootPhoto code to this:

@IBAction func shootPhoto(_ sender: UIBarButtonItem) {
    picker.allowsEditing = false
    picker.sourceType = UIImagePickerControllerSourceType.camera
    picker.cameraCaptureMode = .photo
    picker.modalPresentationStyle = .fullScreen
    present(picker,animated: true,completion: nil)
}

We changed the sourceType property to .camera and specified the cameraCaptureMode property to .photo. The camera, unlike the photo library, is required to be full screen, so we don’t make it a popover, but explicitly a .FullScreen.

Like the library, we have another info.plist entry to make. This time we’ll use XML. Right click the info.plist and in the menus select Open As> Source Code:

2016-06-28_08-48-01

Just below the tag <dict>add this:

<key>NSCameraUsageDescription</key>
<string>Set the background of the app with your beautiful photography</string>

We’re ready to run. However, if you build and run this in the simulator, you will crash. In order to test and use camera code you have to use a real connected device. I connected my iPad Pro and ran the code. When I pressed the photo button, I again get the message about allowing access.

IMG_0379

Tapping OK, I get the camera:

IMG_0381

Take a picture, and the app asks me if I want to keep it.

IMG_0382

The app returns with my photo.

IMG_0383

Preventing the Camera Crash

This all works, but we really want to prevent that crash if there is no camera. Add the highlighted lines to shootPhoto:

 @IBAction func shootPhoto(_ sender: UIBarButtonItem) {
    if UIImagePickerController.isSourceTypeAvailable(.camera) {
        picker.allowsEditing = false
        picker.sourceType = UIImagePickerControllerSourceType.camera
        picker.cameraCaptureMode = .photo
        picker.modalPresentationStyle = .fullScreen
        present(picker,animated: true,completion: nil)
    } else {
        noCamera()
    }
}

Line 2 uses the class method isSourceTypeAvailable to checks if we have a camera. If there is, run the camera. If not explain to the user the device does has noCamera. For that function, we’ll add an alert like this, using UIAlertController:

func noCamera(){
        let alertVC = UIAlertController(
            title: "No Camera",
            message: "Sorry, this device has no camera",
            preferredStyle: .alert)
        let okAction = UIAlertAction(
            title: "OK",
            style:.default,
            handler: nil)
        alertVC.addAction(okAction)
        present(
            alertVC,
            animated: true,
            completion: nil)
    }

Line 2 makes an alert with the proper message. We add an OK action button on lines 6 through 10, then present the alert as a modal in line 11 onwards. With this code, if you run in the simulator, you get this when you attempt to take a picture:

2016-06-28_09-29-39

The Basics, but Wait! There’s more!

Basic UIImagePickerController is relatively easy to implement, but there are a lot of issues it leaves hanging:

  • Hitting no for privacy settings for  accessing the camera or library
  • Dealing with more than one popover
  • Customizing and adding more controls for the camera
  • Adding and playing video
  • Using  and storing pictures
  • Using a UIScrollView to zoom and scroll around a picture.
  • Getting rid of  the memory warnings
  • Wanting more power over my camera controls
  • Sharing pictures with my friends

Many of these could be questions that can be answered in context with the camera app, or outside of that context. In other lessons I’ll be answering them both ways.

The Whole Code

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

import UIKit

class ViewController: UIViewController,
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate
{
    let picker = UIImagePickerController()
    @IBOutlet weak var myImageView: UIImageView!
    
    @IBAction func photoFromLibrary(_ sender: UIBarButtonItem) {
        picker.allowsEditing = false
        picker.sourceType = .photoLibrary
        picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
        picker.modalPresentationStyle = .popover
        present(picker, animated: true, completion: nil)
        picker.popoverPresentationController?.barButtonItem = sender
    }
    
    @IBAction func shootPhoto(_ sender: UIBarButtonItem) {
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            picker.allowsEditing = false
            picker.sourceType = UIImagePickerControllerSourceType.camera
            picker.cameraCaptureMode = .photo
            picker.modalPresentationStyle = .fullScreen
            present(picker,animated: true,completion: nil)
        } else {
            noCamera()
        }
    }
    func noCamera(){
        let alertVC = UIAlertController(
            title: "No Camera",
            message: "Sorry, this device has no camera",
            preferredStyle: .alert)
        let okAction = UIAlertAction(
            title: "OK",
            style:.default,
            handler: nil)
        alertVC.addAction(okAction)
        present(
            alertVC,
            animated: true,
            completion: nil)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        picker.delegate = self
    }

    //MARK: - Delegates
    func imagePickerController(_ picker: UIImagePickerController,
        didFinishPickingMediaWithInfo info: [String : AnyObject])
    {
        var  chosenImage = UIImage()
        chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage //2
        myImageView.contentMode = .scaleAspectFit //3
        myImageView.image = chosenImage //4
        dismiss(animated:true, completion: nil) //5
    }
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
       dismiss(animated: true, completion: nil)
    }

}

How to Use Dictionaries in Swift 3.0

What is a Dictionary

An ordered, indexed array and a unordered dictionary with keys
An ordered, indexed array and a unordered dictionary with keys

Dictionaries and arrays make up the collection types of Swift. Collections are exactly what they say: a collection of values. The difference between an array and a dictionary is how you get the values in and out of the collection. Arrays, as we covered elsewhere, are ordered collections. They have an index which gives their exact place in the collection. I know with an array a value by its position in the array. I know what is before and after my current position by changing the index from its current position.  Arrays must use positive integers in sequence to work this magic.

Dictionaries are unordered collections. No one knows which value comes next, and we really don’t care. The power of a dictionary is we have a key, and the key can pretty much be anything we like that’s a unique value.  One of the most common keys will be strings, but integers and doubles can certainly be used. Dictionaries look up values by the key.

While in an array we pair an index with a value, we pair a key with a value in a dictionary. In Swift, all keys are the same type and all values are the same type in a given dictionary. The value can be of any type, even other objects or collection types. We call the key, then a value returns. Often we find a syntax of key:value for dictionary pairs.

In this lesson for most of these examples, we’ll use a playground. Set up a new playground named SwiftDictionary.

How to Declare a Dictionary

The simplest declaration is to let implicit typing do all the work. We declare a variable or constant and list the key:value pairs in square brackets.

let cookies = ["Chocolate Chip":0.25,"Oatmeal":0.26]

This would create a unmutable dictionary. We can refer to it, but can not change any value. For a mutable dictionary, we could use this:

var mutableCookies = [
    "Macadamia Nut":0.06,
    "Coconut":0.20,
    "Macaron":0.55
]

Dictionaries can have entries on one line or multiple lines. To help with readability in a long list of values, you can list them on multiple lines like this:

var gelato = [
      "Coconut":0.25,
       "Pistachio":0.26,
       "Stracciatella":0.02,
      "Chocolate":0.03,
      "Peanut Butter":0.01,
      "Bubble Gum":0.01
]

You can explicitly type your of key:value pairs. The preferred way to do this is:

let piePrice:[String:Double] = [
   "Apple":3.99,
   "Raspberry":3.35
]

We specify the types in the key value pair surrounded by square brackets. Here we have a string for the key, and a double for the value. There is another declaration which is available but frowned on by Apple.

let piPrice:Dictionary<String,Double> = [
    "Apple Mac":1200.00,
    "Raspberry Pi":45.00
]

This code is identical in function to the previous example but writes out the word Dictionary and uses the <and > characters around the key value pair types.

There are times you need an empty dictionary. In those cases, call the dictionary initializer, which for a dictionary is the key:value pair enclosed in square brackets:

var pieToppings = [String:Double]()

Alternatively, though frowned upon by Apple, you can do this too:

var pizzaSizes = Dictionary<Int, String>()

How to Access a Dictionary

If I wanted to know the price of two slices of pie, the basic syntax is the same for an array, but instead of an index in the square brackets we place the key. We’ve already declared pie prices per slice. How much is an apple pie slice? Like an array’s index, you place the key on brackets:

print(piePrice["Apple"])

How much for a gram of coconut gelato?

print(gelato["Coconut"])

The code piePrice["Apple"] prints the price of a slice of apple pie and the gelato returns the price per gram.

Optional(3.99)
Optional(0.25)

You’ll notice the values returned are optionals. Returned dictionary values are always optionals. At any given time, I do not know if there is a certain type of pie or not in my dictionary. If I tried piePrice["Coconut Cream"] I need some way to tell me that there is no coconut cream pies in my dictionary. Type

print(piePrice["Coconut Cream"])

The playground print nil to the console. Swift makes the return value of a dictionary an optional value, so it will return nil if there is no such entry.

If your dictionary is a constant, you know exactly what’s there. In that case, we can use a forced unwrap to calculate the price for 100 grams of Coconut gelato:

print(gelato["Coconut"]! * 100.0)

If you have a dictionary in a variable, where you may add or delete elements from the dictionary, make sure you check for nil. For two slices of apple pie, we could write this:

let slices = 2.0
if piePrice["Apple"] != nil {
    let extPrice = piePrice["Apple"]! * slices
    print("\(slices) slices of Apple Pie is \(extPrice)")
}

or more compactly with optional chaining

if let price = piePrice["Apple"]{
    let extPrice = price * slices
    print("\(slices) slices of Apple Pie is \(extPrice)")
}

If you are not familiar with optionals, refer to the Ten Points for Using Optionals post to get yourself up to speed.
Of course, keys are not always literal. We can use a variable or constant. Here we have code to include a constant for the gelato type.:

let scoopSize = 100.0 //grams
let gelatoType = "Pistachio"
if let price = gelato[gelatoType] {
    let extPrice = price * scoopSize
    print("\(scoopSize) grams of "
        + gelatoType
        + " gelato is $\(extPrice)")
}

Change a Dictionary

Suppose we have a dictionary of prices for pizza by a unit area like a square inch, square cm or such. Add this dictionary:

var toppings = ["Pepperoni":0.25,
    "Sausage":0.26,
    "Onions":0.02,
    "Green Peppers":0.03,
    "Cheese":0.01
]

Our cost of Sausage and Cheese increases, and we need to change prices in our dictionary. We can change the value with assignment:

toppings["Sausage"] = 0.29
print(toppings["Sausage"])

Assignment uses subscript syntax and simply changes the value. We can also do this:

toppings.updateValue(0.2, forKey: "Cheese")
toppings["Cheese"]

Line 2 uses the updateValue(value:,forKey:) which does the same as line 1 but the function returns a value. You can write line 2 like this as well:

let oldTopping = toppings.updateValue(0.02,forKey: "Cheese")
if oldTopping == nil{
    print("Key not found")
}

The method returns the value as an optional before we changed it. If there is no key, updateValue(value:,forKey:) returns nil. There is a check to tell the developer that this was not in the dictionary. Unlike an array, a key not found is not a fatal error. Instead it adds an element to the dictionary. If we change a value that isn’t there, such as:

let anotherTopping = toppings.updateValue(0.15,forKey: "Gorgonzola")
toppings["BBQ Chicken"] = 0.24

Swift will add the new entries for Gorgonzola and BBQ Chicken to the dictionary. There are times that we may not want to add a new element, but give an error or do something else. Using updateValue(value:,forKey:)  we can  check for nil. If nil, we  execute code to delete the new value and do whatever we need to for an unknown key.

To delete a dictionary entry, you can set its value to nil:

gelato["Bubble Gum"] = nil
print(gelato)

You can also use the removeValue(forKey:) method

let deletedTopping = gelato.removeValue(forKey:"Peanut Butter")
print(deletedTopping)
print(gelato)

The method removeValue(forKey:) deletes the element. Like updateValue(value:,forKey:) the method returns the value deleted. When deleting an element that does not exist, the method tells you by returning nil, and the subscript syntax does nothing. If you need to know there was no key present, use the method version.

Iterating Dictionaries

Sometimes we need to list or change everything in a dictionary. You can use the for..in loop to do that. If you need to print a list of all entries and their values in the toppings dictionary, create a tuple with identifier names:

for (myKey,myValue) in toppings {
    print("\(myKey) \t \(myValue)")
}

We don’t have optionals involved this time, since all values are existing values. If we need to iterate through just keys or values we can do that using the .keys or .values.
To list all the keys, we could do this:

for myKey  in toppings.keys{
    print ("Key =  \(myKey)")
}

The list that prints may not be in the same order as the list we entered. Dictionaries are unordered collections, and will be near random in order.

We can also do the same thing with the values, but we generally don’t for a really good reason.

for myValue  in toppings.values{
    print ("Value =  \(myValue)")
}

The iterator myValue is a constant inside of the loop. You cannot change this value. For example, if we need to make a 10% increase in all prices for our toppings we can’t write:

for myValue  in toppings.values{
    myValue = myValue * 1.10
}

The compiler would tell us:

Cannot assign to value: 'myValue' is a 'let' constant.

We cannot use the values in the for..in loop to do this. If we want to change values in the loop, we iterate over the keys.  Do the calculation directly to the dictionary value based on the iterated key.

//make a 10% price increase
for myKey  in toppings.keys{
    toppings[myKey] = toppings[myKey]! * 1.10
}

Sometimes we need an array of the keys or values, since a particular API, such as UITableViewController, requires an array. We can use the .keys or .values to create an array:

var valuesArray = [Double](toppings.values)
let keyArray = [String](toppings.keys)

Passing a Dictionary as a Parameter

Sometimes you will need to pass a lot of values of the same type in a method through a parameter. Instead of making a dozen parameters, you can store their values in a dictionary.

func myFunction(dictionary:[String:Double],key:String)-> Double?{
    return dictionary[key]
}

myFunction(gelato,key: "Coconut")

While this really does nothing special, it make a good prototype function. You can see how to declare a dictionary in a function. Declare the dictionary like you would in an explicit let or var statement. Note here I made the return value Double? to let the optional value through.

Here’s One more example returning an extended price.

func extendedPrice(
    dict:[String:Double],
    key:String,
    units:Double
) -> Double?{
    guard let price = dictionary[key] else {return nil}
    return price * units
}

This function has three parameters: the dictionary of the form [String:Double], the key and some units to multiply. Try these:

print( extendedPrice(dict: toppings, key: "Pepperoni", units: 22.0))
print(extendedPrice(dict: gelato, key: "Chocolate", units: 150.0))
print(extendedPrice(dict: piPrice, key: "Android", units: 2.0))

Using different dictionaries, you get results like this, including nil for the not found Android case

Optional(6.0500000000000007)
Option

AnyObject and Dictionaries

Strongly typed dictionaries are the Swift Standard. the value is always the same type. Yet there are some dictionary situations that need multiple types. In some situations, we need something like a struct or class that stores several properties, but it is so temporary we don’t want to declare the class or struct.This is very common in Objective-C written API’s. Consider this function:

func dessertAssembly(cookie: String, iceCream:String) -> [String:String]?{
    let iceCreamServing = 200.0
    var returnDictionary = [String:String]()
    returnDictionary["Dessert"] = cookie + " with " + iceCream
    guard let cookiePrice = mutableCookies[cookie]
        else {return nil}
    guard let gelatoPrice = gelato[iceCream]
        else {return nil}
    returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //Double not String
    return returnDictionary
}

We’d like to return for the key Dessert a String and for Price a Double. Swift forces us to return for both keys a string and will give us an error when we assign a Double to our dictionary value.

Binary operator '+' cannot be applied to two 'Double' operands

The compiler expects a string here and thinks + is string concatenation, not addition. It complains you can’t concatenate cookiePrice and gelatoPrice * iceCreamServing since they are doubles.

We could convert the price calculation to a string. We can also do something else: use a dictionary of type [String:AnyObject]. AnyObject allows assignment of any object. If we change the code to this, it compiles without errors:

  
func dessertAssembly(cookie: String, iceCream:String) -> [String:AnyObject]?{
    let iceCreamServing = 200.0
    var returnDictionary = [String:AnyObject]()
    returnDictionary["Cookie"] = cookie + " with " + iceCream
    guard let cookiePrice = mutableCookies[cookie]
        else {return nil}
    guard let gelatoPrice = gelato[iceCream]
        else {return nil}
    returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //double not a string
    return returnDictionary
}

Try this:

let myDessert = dessertAssembly(
    cookie: "Chocolate Chip",
    iceCream: "Coconut")
print(myDessert?["Price"])

and we get the price.

As a precaution, many developers downcast to the correct type instead of AnyObject, so they would use

print(myDessert?["Price"] as! Double)

In this example that would need unwrapping the options first, but that my not be a problem in other cases.

Working with TableViews and Dictionaries

As TableViewControllers use IndexPath, they are best used with arrays, not dictionaries. What if you want to use a dictionary with table views? The best way is to create an array of keys, and then use that array. In the code that defined the dictionary, add a computed property to get the keys, in this case for gelato

var keyList:[String] {
    get{
        return [String](gelato.keys)
    }
}

In tableview: cellForRowAt indexpath: for your table, you access your key from keyList, and get the value using that key.

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let row = indexPath.row //get the array index from the index path
    var cell = tableView.dequeueReusableCell(withIdentifier:"tableCell") //make the cell
    if cell == nil {
        cell = UITableViewCell(style: .value1, reuseIdentifier: "cell")
    }
    let myRowKey = keyList[row] //the dictionary key
    cell?.textLabel?.text = myRowKey
    let myRowData = gelato[myRowKey] //the dictionary value
    cell?.detailTextLabel?.text = String(format: "%6.3f",myRowData!)
    return cell!
}

I used the dictionary  starting in the highlighted line 7. Dictionaries are non-ordered, and for an ordered object like a table view, I need something to correspond in an ordered fashion to my table view’s indexPath. I used the computed property typelist, which is an array of keys. This is a good way to get a dictionary ordered when needed. Make an array of keys and iterate over the array pointing to the dictionary. In the highlighted Line 9 I take the key and get the price.

Playgrounds on iPad running the table.
Playgrounds on iPad running the table.

We’ve kept the value’s type to something simple. You can declare a value  as a class instead. In my pizza ordering system, I could have a class describing a pizza order, and use a key to hold the order number. Often people use sequential numbers for the order number. If so, an array might work there. Some systems hide data in the order number, like the date, time or server ID.

Hopefully this was a helpful introduction to Dictionaries in Swift.

The Whole Code

Here is a file of the playground for this lesson. While typing in the code above is very useful you can use this to experiment with dictionaries.

//: Playground - noun: a place where people can play

import UIKit

let cookies = ["Chocolate Chip":0.25,"Oatmeal":0.26]

var mutableCookies = [
"Macadamia Nut":0.06,
"Coconut":0.20,
"Macaron":0.55
]

var gelato = [
    "Coconut":0.025,
    "Pistachio":0.026,
    "Stracciatella":0.002,
    "Chocolate":0.003,
    "Peanut Butter":0.001,
    "Bubble Gum":0.001
]

let piePrice:[String:Double] = [
    "Apple":3.99,
    "Raspberry":3.35
]



let piPrice:Dictionary<String,Double> = [
    "Apple Mac":1200.00,
    "Raspberry Pi":45.00
]

var pieToppings = [String:Double]()


var pizzaSizes = Dictionary<Int, String>()

print(piePrice["Apple"])
print(gelato["Coconut"])
print(piePrice["Coconut Cream"])

print(gelato["Coconut"]! * 100.0)

let slices = 2.0
if piePrice["Apple"] != nil {
    let extPrice = piePrice["Apple"]! * slices
    print("\(slices) slices of Apple Pie is \(extPrice)")
}

if let price = piePrice["Apple"]{
    let extPrice = price * slices
    print("\(slices) slices of Apple Pie is \(extPrice)")
}

let scoopSize = 100.0 //grams
let gelatoType = "Pistachio"
if let price = gelato[gelatoType] {
    let extPrice = price * scoopSize
    print("\(scoopSize) grams of "
        + gelatoType
        + " gelato is $\(extPrice)")
}

//: # Changing a Dictionary

var toppings = ["Pepperoni":0.25,
                "Sausage":0.26,
                "Onions":0.02,
                "Green Peppers":0.03,
                "Cheese":0.01
]

toppings["Sausage"] = 0.29
print(toppings["Sausage"])

toppings.updateValue(0.2, forKey: "Cheese")
print(toppings["Cheese"])


let oldTopping = toppings.updateValue(0.02,forKey: "Cheese")
if oldTopping == nil{
    print("Key not found")
}

let anotherTopping = toppings.updateValue(0.15,forKey: "Gorgonzola")
toppings["BBQ Chicken"] = 0.24

gelato["Bubble Gum"] = nil
print(gelato)

let deletedTopping = gelato.removeValue(forKey:"Peanut Butter")
print(deletedTopping)
print(gelato)

for (myKey,myValue) in toppings {
    print("\(myKey) \t \(myValue)")
}

for myKey  in toppings.keys{
    print ("Key =  \(myKey)")
}


//: Commented out since myValue is a constant
/*for myValue  in toppings.values{
    myValue = myValue * 1.10
}
*/

//: Correct way to itereate over key values
//make a 10% price increase
for myKey  in toppings.keys{
    toppings[myKey] = toppings[myKey]! * 1.10
}

//: Making an array of keys and values
var valuesArray = [Double](toppings.values)
let keyArray = [String](toppings.keys)


//: #Parameters

func extendedPrice(dictionary:[String:Double],key:String,units:Double) -> Double?{
    guard let price = dictionary[key] else {return nil}
    return price * units
}

print( extendedPrice(dictionary: toppings, key: "Pepperoni", units: 22.0))
print(extendedPrice(dictionary: gelato, key: "Chocolate", units: 150.0))
print(extendedPrice(dictionary: piPrice, key: "Android", units: 2.0))

//: AnyObject in action 

//: This does not compile
/*
func dessertAssembly(cookie: String, iceCream:String) -> [String:String]?{
    let iceCreamServing = 200.0
    var returnDictionary = [String:String]()
    returnDictionary["Cookie"] = cookie + " with " + iceCream
    guard let cookiePrice = cookies[cookie]
        else {return nil}
    guard let gelatoPrice = gelato[iceCream]
        else {return nil}
    returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //double not a string
    return returnDictionary
}
 */


//this does compile
func dessertAssembly(cookie: String, iceCream:String) -> [String:AnyObject]?{
    let iceCreamServing = 200.0
    var returnDictionary = [String:AnyObject]()
    returnDictionary["Cookie"] = cookie + " with " + iceCream
    guard let cookiePrice = cookies[cookie]
        else {return nil}
    guard let gelatoPrice = gelato[iceCream]
        else {return nil}
    returnDictionary["Price"] = cookiePrice + gelatoPrice * iceCreamServing //double not a string
    return returnDictionary
}

let myDessert = dessertAssembly(
    cookie: "Chocolate Chip",
    iceCream: "Coconut")
print(myDessert?["Price"])

//: Table View Controller

var keyList:[String] {
    get{
        return [String](gelato.keys)
    }
}
//comment out on IBM Sandbox. 
class TableViewController:UITableViewController{
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return gelato.count
    }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let row = indexPath.row //get the array index from the index path
    var cell = tableView.dequeueReusableCell(withIdentifier:"tableCell") //make the cell
    if cell == nil {
        cell = UITableViewCell(style: .value1, reuseIdentifier: "cell")
    }
    let myRowKey = keyList[row] //the dictionary key
    cell?.textLabel?.text = myRowKey
    let myRowData = gelato[myRowKey] //the dictionary value
    cell?.detailTextLabel?.text = String(format: "%6.3f",myRowData!)
    return cell!

}

}

import PlaygroundSupport
let vc = TableViewController()
PlaygroundPage.current.liveView = vc
PlaygroundPage.current.needsIndefiniteExecution = true



How to Use Property Lists (.plist) in Swift

This will be the first in a series of lessons on persistence in iOS applications. Persistence is keep data around even if your app ends. As we’ll see in this series there are many techniques to save and read data. One Common one Apple uses frequently in Xcode is the Property list, or plist, named for its extension. In this first lesson we’ll look at how to make, modify and read property lists, and when to use them.

Understanding Property Lists

Open a new project in Xcode with a Single View template using Swift as the language. Save the project as SwiftPropertyListDemo. In the Project inspector, you will find a file info.plist. Select this file.

2016-02-10_05-52-25

You will find a screen looking like this:

2016-02-10_05-53-23

Many of the settings about your app the compiler needs to know is here. That’s one of the uses for property lists: to keep settings between runs of your application. Right click on the info.plist file name. In the menu that appears select Open as>Source Code

2016-02-10_06-01-03

You will get a very different looking screen of XML code starting like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>

In most cases, a .plist file is a XML file stored as a dictionary. We can edit property lists either in the property list editor or in the plists. You’ll notice there are key tags and string tags and one dict tag. This makes a dictionary of [String:String]

Understanding Basic XML

If that didn’t make sense to you, Lets discuss some basic XML. XML like HTML is a text file with start and end tags indicated by the characters < and > with an ending indicator of /. Unlike HTML, there are no reserved tags. The author makes up their own. In some program those tags indicate data. You’ll find XML in things like RSS feeds and podcast metadata. As we’ll discuss in a later lesson, some databases can be XML files. Microsoft Word’s .xdoc file format is actually a XML document.

All XML documents start with a header stating they are a XML document and what kind of document. In the above property list and all property lists we have the following header:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

In a property list, we have the following tags available

property list XML tags

Apple’s internal property list is a dictionary with key:value pairings. You can see here most of the entries are of the form of a key and a string, but there are Bool and array as well:

<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
...
        <key>LSRequiresIPhoneOS</key>
	<true/>
...
        <key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>

</dict>

Making Your Own Property Lists

You can make your own property lists. Within Xcode, there is a file template. Press Command-N to get a new file. Under iOS Resource select Property list

2016-02-10_07-06-42

Name the property list data, and store it in the SwiftPropertyListDemo group folder. You will see a blank property list in a dictionary.

2016-02-10_07-10-41

Hover over the root entry and you will find a + button to add an entry

2016-02-10_07-13-00

Click the button and a new entry appears, with a default type of String

2016-02-10_07-15-25

Click the type selector indicating String, and you will find the following selections

2016-02-10_07-15-26

Select Number. Change the value to 0.3, and the key to Red

2016-02-10_07-15-27

Press root’s add button two more times and add a Green value of 0 and Blue as a blank string.

2016-02-11_06-55-24

We’ll use these three values to save a color. While there is a better way to save colors that will be covered in another lesson, this is a simpler way for us to set a color from a property list.

Click the + button on Root. Make an entry with the key Pies and a type array. You’ll get an arrow next to the key Pie . Press the + button on the Pies key. You’ll get another entry.

2016-02-11_07-06-39

The + on any entry makes another entry below it. Since we do not want this entry, click the on the key to delete it. Notice the triangle next to Pies click it so it points down. In order to get elements in a dictionary or array you must click this to point down. Click the + on the pie entry four times to get four elements to your array.

2016-02-11_07-11-58

Keeping the array elements as strings add four pies to the property list.

2016-02-11_07-14-08

Click the Root + button. Add a boolean entry with the key Inverse Colors set to NO.

You can re-arrange your items by click and dragging up and down. You’ll see a blue line indicating where the item will be inserted. Re-arrange the keys like this. As far as the compiler is concerned you don’t have to do this, but it is easier for humans to read and understand.

2016-02-11_07-20-32

Editing the XML Property List

Let’s look at the XML for our property list. Right Click data.plist and select Open As>Source Code. We get the following code

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Inverse Color</key>
	<false/>
	<key>Red</key>
	<real>0.3</real>
	<key>Green</key>
	<key>Green</key>
	<integer>0</integer>
	<key>Blue</key>
	<string></string>
	<key>Pies</key>
	<array>
		<string>Cherry</string>
		<string>Apple</string>
		<string>French Silk</string>
		<string>Coconut Cream</string>
	</array>
</dict>
</plist>

Green is an integer and Blue is a string. The editor assumes an integer if you do not include a decimal point. Let’s change both real and add a value of 0.3 to the Blue entry. Change this

<key>Green</key>
<integer>0</integer>
<key>Blue</key>
<string></string>

to this

<key>Green</key>
<real>0.0</real>
<key>Blue</key>
<real>0.3</real>

Let’s add two more pies to our array, under the entry for Coconut Cream add the following:

<string>Blueberry</string>
<string>Boston Cream</string>

Switch back to the editor by selecting Open As> Property list and you will see your changes to the property list.

2016-02-11_07-55-22

Setting up a Demo Application

Let’s set up a simple table view application to use our new property list. If you are not familiar wth table view controllers I’d suggest reading this first. Go to the story board and delete the current view controller. Drag a table view controller to the storyboard. In the attribute inspector, click on Is Initial View Controller. Select the table view cell and change the Style to Basic and the Identifier to cell.

2016-02-11_07-26-20

Now go to the View Controller. Change the view controller to subclass UITableViewController instead of View Controller

class ViewController: UITableViewController {

Go back to the storyboard and select the table view controller. In the Identity inspector, set the Class to View Controller.

Go to your view controller code. Add the following properties with some test data:

var isColorInverted = true
var color1 = UIColor(
    red: 0.3,
    green: 0.3,
    blue: 0.0,
    alpha: 1.0)
let color2 = UIColor(
    white: 0.9,
    alpha: 1.0)
var pies = [
    "Pizza",
    "Chicken Pot",
     "Shepherd's"
]    

Add the delegates and data sources. At the bottom of the class add this:

 //MARK: - Table View Data sources and Delegates
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pies.count
    }
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Pies"
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        cell.textLabel?.text = pies[row]
        if isColorInverted { //invert colors by flag status
            cell.backgroundColor = color2
            cell.textLabel?.textColor = color1
        }else{
            cell.backgroundColor = color1
            cell.textLabel?.textColor = color2
        }
        return cell
        
    }


We’ll display the value of the array in the cells and change the color of the table view cell depending on the value of isColorInverted. Now build and run this to make sure the app works with the test data.

2016-02-11_07-29-52

Reading a .plist in Swift

Add the following above the viewDidLoad method.

func readPropertyList(){
    
}

We’re going to make this a function that we’ll call in our viewDidLoad to set the properties for the class according to the property list. Add the following property to the new function:

var format = NSPropertyListFormat.XMLFormat_v1_0 //format of the property list

The variable format is the format of the property list. While XML is the most common there are a few other types available all listed in the enumeration NSPropertyListFormat. This will be used in a parameter later on, but needs an initial value. The method we’ll run later may change format to a different format during the conversion process so it will be a pass through variable. Add under this property

var plistData:[String:AnyObject] = [:]  //our data

We’ll use plistData to store the dictionary entries of the property list. I’m making an assumption here you may not want to make. For almost all cases a property list will be a dictionary, so I’m setting this up for a dictionary. That might be a bad assumption and you can alternately write var plistData:AnyObject! = nil. or whatever format you are expecting from the property list. Now add this line

let plistPath:String? = NSBundle.mainBundle().pathForResource("data", ofType: "plist")!

This finds and returns the path of our list from the main bundle. I’m assuming it is in the main bundle here or there is only one data.plist in the bundle. Both could cause errors. For simplicity I made sure it was. You should add more robust code to find it where it may be hiding or if there are duplicates. Now add this

 let plistXML = NSFileManager.defaultManager().contentsAtPath(plistPath!)!

The constant plistXML is the XML data as type NSData format. The function may return nil if the file is not found. Now add this under it

do{
plistData = try NSPropertyListSerialization.propertyListWithData(plistXML,
    options: .MutableContainersAndLeaves, 
    format: &format) 
    as! [String:AnyObject]
}
catch{
    print("Error reading plist: \(error), format: \(format)")
}        

Here we convert the XML data to a dictionary. There is a class, NSPropertyListSerialization which along with the class method propertyListWithData does the conversions. It contains several parameters to discuss. The first parameter is the data we will convert with is of Type NSData, which we will discuss in more detail in the next lesson in this series. The second parameter is a constant from the enum NSPropertyListMutabilityOptions. This parameter has three possible options which affect the mutability of the dictionary created. It’s a feature for Objective-C, not Swift. In Swift it’s meaningless. The parameter format is a pass through variable, so needs the &format. If we did not use it in the print statement, we could also put

While I’ve been fast and loose with error checking for this demo, this is the one place I’m careful. If you edit your plist in XML on a text editor, you have the chance of introducing syntax errors which will mess up everything. For that reason, I use Swift’s error control do...try...catch clause to catch syntax errors in the XML.

If this step is successful, we have in plistData a dictionary of our property list. Add the following code to assign the Bool value in the do block

isColorInverted = plistData["Inverse Color"] as! Bool

The value of dictionary is of type AnyObject. For each value we need to downcast it to assign it properly. For isColorInverted, that is a Bool. For the XML tags real and integer, it can be any real or integer type. Add this code :

let red = plistData["Red"] as! CGFloat
let green = plistData["Green"] as! CGFloat
let blue = plistData["Blue"] as! CGFloat
color1 = UIColor(red: red, green: green, blue: blue, alpha: 1.0)

We did not have to downcast to Float and then convert to a CGFloat. We can Downcast directly to CGFloat. Add this to assign the array pie

pies = plistData["Pies"] as! [String]

The function is now complete and will assign to the properties of the class values from the the property list. Change viewDidLoad to use it.

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        readPropertyList()
    }

Build and run. You will get different results than your first run

2016-02-11_07-42-07

When to Use plists

I’m conservative with using plists. While you can write plists, I never do. I’m of the thought that property lists should be a read-only at run time. While there are several reasons for that, my biggest is there are much better ways to save data than plists. I use them for two things only. Instead of setting global constants, I’ll make a plist and load it into the classes that need it. A small data file is a lot easier to change than finding a constant in a mass of code. Secondly, I use it as I did here to load tables and pickers with static data. Instead of making a huge amount of code to set arrays, I make it an external file. These both are read-only operations. In the lessons the follow, we’ll look at some of the ways to both read and write data.

If you liked this lesson and would like to be notified about more lessons, subscribe to the Make App Pie NewsletterWhich sends a new iOS lesson every Thursday and news and sneak previews of upcoming works every Monday

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  SwiftPropertyListDemo
//
//  Created by Steven Lipton on 2/10/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UITableViewController {
    var isColorInverted = true
    var color1 = UIColor(red: 0.3, green: 0.3, blue: 0.0, alpha: 1.0)
    let color2 = UIColor(white: 0.9, alpha: 1.0)
    var pies = ["Pizza","Chicken Pot","Shepherd's"]
    
    func readPropertyList(){
        
        var format = NSPropertyListFormat.XMLFormat_v1_0 //format of the property list
        var plistData:[String:AnyObject] = [:]  //our data
        let plistPath:String? = NSBundle.mainBundle().pathForResource("data", ofType: "plist")! //the path of the data
        let plistXML = NSFileManager.defaultManager().contentsAtPath(plistPath!)! //the data in XML format
        do{ //convert the data to a dictionary and handle errors.
            plistData = try NSPropertyListSerialization.propertyListWithData(plistXML,options: .MutableContainersAndLeaves,format: &format)as! [String:AnyObject]
            
            //assign the values in the dictionary to the properties
            isColorInverted = plistData["Inverse Color"] as! Bool
            
            let red = plistData["Red"] as! CGFloat
            let green = plistData["Green"] as! CGFloat
            let blue = plistData["Blue"] as! CGFloat
            color1 = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
            
            pies = plistData["Pies"] as! [String]
        }
        catch{ // error condition
            print("Error reading plist: \(error), format: \(format)")
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        readPropertyList()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    //MARK: - Table View Data sources and Delegates
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pies.count
    }
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Pies"
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        cell.textLabel?.text = pies[row]
        if isColorInverted { //invert colors by flag status
            cell.backgroundColor = color2
            cell.textLabel?.textColor = color1
        }else{
            cell.backgroundColor = color1
            cell.textLabel?.textColor = color2
        }
        return cell
        
    }

}


data.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Inverse Color</key>
	<false/>
	<key>Red</key>
	<real>0.3</real>
	<key>Green</key>
	<real>0</real>
	<key>Blue</key>
	<real>0.3</real>
	<key>Pies</key>
	<array>
		<string>Cherry</string>
		<string>Apple</string>
		<string>French Silk</string>
		<string>Coconut Cream</string>
		<string>Blueberry</string>
		<string>Boston Cream</string>
	</array>
</dict>
</plist>

How to Use enum and Enumerations in Swift

Often code needs some way to describe a discrete set of cases. You might need values for breakfast lunch and dinner for example. How we configure a view might be set by one of a few choices. There are many types of UIButtons we can use such as system, custom, and detail disclosure. We would declare them like this:

let systemButton = UIButton(type:UIButtonType.System)
let customButton = UIButton(type:.Custom)
let detailDisclosureButton = UIButton(type:.DetailDisclosure)

After the type: parameter we have an enumeration, often called an enum which is the declaration keyword for an enumeration. Enumerations are often misunderstood, and often give powerful ways to control and document code easily. In this lesson, we’ll begin to explore the basic uses for enumeration.

Using IBM’s Swift Sandbox

We won’t write a full app for this lesson. Instead we’ll use a handy tool for exploring the Swift language anywhere there is an internet connection and web browser. In December 2015, Apple released the open source Swift version (Swift 2.2). It does not include the API’s of UIKit, but does have all the Foundation framework. IBM in partnership with Apple launched a website-based playground (which they confusingly call a sandbox) for testing code. I’ll use the IBM Sandbox for this lesson, which I find faster than Xcode’s playground, and you can use it on your iPad or even PC since you don’t need Xcode, just a Web browser. You can certainly use Xcode’s playground if you want.

Open your favorite web browser and navigate to http://swiftlang.ng.bluemix.net/#/repl. You’ll get a website looking like a playground:

2016-01-20_05-28-52.png

Depending on your tastes you can change the skin from dark to light. Click the settings gear on the sandbox, then select the Light Theme.

2016-01-20_05-29-05.png

You’ll get a light color scheme.

2016-01-20_05-29-24.png

You’ll see a snippet of code in the left side code window

//  TODO: Write some awesome Swift code, or import libraries like "Foundation",
//  "Dispatch", or"Glibc"

print("Hello world!")

At the lower center, you’ll find the Run button. Click that button to make your code compile and run. We get the classic Hello World message. If you have syntax errors, they show in the Results side of the page. Remove the quote mark after the exclamation mark from the current code and run again. You get several errors starting with:

/swift-execution/code-tmp.swift:4:7: error: unterminated string literal

Fix the error and run again. The errors are replaced by the Hello World output

Some Code to Fix

To explain enums, let’s make some code that should use it, but doesn’t. Since I’m writing this in January, a lot of people are obsessed with weight loss now. Let’s make a calorie counter to help them out. Delete the source code in the sandbox, and add this code in its place:

import Foundation
var  caloriesTotal = [0,0,0,0]

We made an 4-element array to keep totals for calories for each meal. The first position of this array is breakfast, then lunch, dinner and snack. Add this function to the code:

func addCaloriesForMeal(meal:Int,calorie:Int){
	caloriesTotal[meal] +=  calorie
	print ( "Calories for meal \(meal) is \(caloriesTotal[meal])")
	if meal == 3 {
		print("Try not to snack")
	}
	if (meal == 0) && (caloriesTotal[meal] > 100){
		print("Breakfast is the most important meal of the day. Eat more breakfast.")
	}
}

The code adds calories, gives us feedback to the current calorie count for that meal, and a few comments about our current diet. Without comments, this would be difficult code to understand, and even code for. I have to remember that 0 is breakfast and 3 is snacks. Someone else reading this code would have an even more difficult time. Add the following to call the function:

addCaloriesForMeal(0,calorie:50)
addCaloriesForMeal(1,calorie:280)
addCaloriesForMeal(2,calorie:650)
addCaloriesForMeal(3,calorie:1150)

Run the code and it works.


Calories for meal 0 is 50
Breakfast is the most important meal of the day. Eat more breakfast.
Calories for meal 1 is 280
Calories for meal 2 is 650
Calories for meal 3 is 1150
Try not to snack

The code works. It’s just very difficult to work with

Using Enum

What if we could change the values of 0,1,2,3 with words? We could do this with constants like this:

let breakfast = 0
let lunch = 0
let dinner = 0
let snacks = 0

Even better, we can define a special type that has these constants as the only value of the type. We could use that type anywhere within the scope. This is an enumeration. Add the following code above the var in your current code.

enum Meal:Int{
	case Breakfast = 0
	case Lunch = 1
	case Dinner = 2
	case Snack = 3
}

The first line declares an enumeration type called Meal. We declare Meal to be representing an integer value. The next lines contain the values we are representing. We can now use the enumeration as a constant.To make clear these are not variables, values in an enum have their first letter capitalized. In Swift, you’ll find two ways of using this constant. If there is no context as to type, you will see Meal.Breakfast. If there is a context, such as in a declared parameter or comparison to a value of the same enumeration type, you can write .Breakfast .

Change the declaration for our function to this:

func addCaloriesForMeal(meal:Meal,calorie:Int){

We changed the type for the meal to our enumeration. For most cases this will be all you need to do.

We can now change our Boolean statements to be better documented in the code. Change the first if to this:

 if meal == .Snack {

This is much easier to read than before

The rawValue Property

There is one wrinkle to enums. Unlike a constant or variable, the value of an enum cannot directly assign its value to something else. Our current array index

caloriesTotal[meal] +=  calorie

Would cause an error:

/swift-execution/code-tmp.swift:11:15: error: cannot subscript a value of type '[Int]' with an index of type 'Meal'

Meal is a different type, and Swift requires an Int here. We have a property of enums rawValue. After our function declaration add this line:

let mealIndex = meal.rawValue  //get the raw value of the enumeration

Now change the two lines under it to this:

caloriesTotal[mealIndex] +=  calorie
print ("Calories for meal \(mealIndex) is \(caloriesTotal[mealIndex])")

We have one more line to change in our function. Change the breakfast if statement

if (meal == .Breakfast) && (caloriesTotal[mealIndex] < 100){

Calling the function is a lot easier now. Replace the function calls with the following:

addCaloriesForMeal(.Breakfast,calorie:50)
addCaloriesForMeal(.Dinner,calorie:650)
addCaloriesForMeal(.Snack,calorie:1150)

Now run the code. We get similar results, but our code is a lot easier to read

Using Switch…Case with Enumeration

We’ve already evaluated situations with if statements. Often we will have to do something different for every case in an enumeration. This is where the switch...case decision structure comes in handy. In our code, it’s more user friendly to display breakfast, lunch, dinner, or snack instead of a meal number. Add this code just below the caloriesTotal addition:

//Add a switch statement for a meal string
	var mealString = ""
	switch meal{
	   case .Breakfast:
	    mealString = "breakfast&"
	   case .Lunch:
	    mealString = "lunch"
	   case .Dinner:
	    mealString = "dinner"
	   case .Snack:
	    mealString = "snack"
	}

We first make a new variable for our string. Then we use the switch stating we are looking at options for our meal variable. Within the brackets, we put case and one of our enumeration values. Under that case is code which assigns the value of the strong. Unlike an if...else clause, case does not need a block for these statements. It assumes your next case is the end of your block. However, you must be exhaustive. All values must be listed. If you do not list every value, you must include a default case.

Now that we have a string to use, change the print to this:

print ( "Calories for " + mealString + " is \(caloriesTotal[mealIndex])")

Run your code. You will get a different output:


Calories for breakfast is 50
Breakfast is the most important meal of the day. Eat more breakfast.
Calories for dinner is 650
Calories for snack is 1150
Try not to snack

Methods in Enumerations

There’s a problem with this switch statement. It is not very adaptable. For any use of my enumeration, I might want to get that string. Enumerations, like classes, can have methods. Under case Snack = 3 in the enum add this:

func mealName() -> String{

	return mealString
}

Cut and paste the mealString declaration and the switch statement between the func declaration and the return. In the switch, change mealString to self. Your code should look like this:

func mealName() -> String{
	var mealString = ""
	switch self{
    		case .Breakfast:
			mealString = "breakfast"
		case .Lunch:
			mealString ="lunch"
		case .Dinner:
            mealString = "dinner"
        	case .Snack:
            	mealString = "snack"
    		}
    		return mealString
}

Change the print to this:

print ( "Calories for " + meal.mealName() +" is \(caloriesTotal[mealIndex])")

We called the function mealName which returns our string. Run and we get the same results.

Default Values for Int Enums

Change the enum cases to this, then run again:

enum Meal:Int{
	case Breakfast = 0
	case Lunch
	case Dinner
	case Snack

You get the same results. An enum for an Int assumes that each value will increment by one, and you have only assigned the starting value of 0. Now try this:

enum Meal:Int{
	case Breakfast
	case Lunch
	case Dinner
	case Snack

When you run this, the code still works. Only for a type of Int, Swift’s enums assume a value of 0 and will increment by 1. This of course begs the question: Can you use other types, or no types at all?

Using Other Types

Open a new sandbox in your web browser. Remove the base code and add this:

import Foundation
// Use of other types
//  any Int Float, Double, or String Works here.
enum Meals:String{
    case Breakfast = "breakfast"
    case Lunch = "lunch"
    case Dinner = "dinner"
    case Snack = "snack"
}
print(Meals.Breakfast.rawValue)

Run this and you will get breakfast. We’ve so far used integer values for the enum. We can use it to store strings like we did here. Change the type declaration to the appropriate type.

enum Meals:String

You can use any basic type for an enum raw value including Int, Float, Double, or String. You cannot use a complex values such as an array, class or another enum.

The Bare-Bones enum

Often we don’t need a value constant for our enumeration. We need a discrete set of values only. This basic enum does that:

Meals{
    case Breakfast
    case Lunch
    case Dinner
    case Snack
}
print (Meals.Breakfast)

Try this code in the sandbox. You will once again get Breakfast in the output.There is a very important caution using this. An enum like this has no rawValue. Its only value is one of its four cases. Breakfast is a value, but it is not a String. You’ve created a type that only has the four values.

If you want to do this in a shorter version you can do this:

 Meals{
    case Breakfast,Lunch,Dinner,Snack
}
print (Meals.Breakfast)

The short version works for Int enums with default values

enum Meals:Int{
    case Breakfast,Lunch,Dinner, Snack
}
print(Meals.Lunch)
print(Meals.Lunch.rawValue)

Associative Enumerations

So far, enumerations have been used as a constant. There is one more case that can have assigned values. Called Associative Enumerations they associate any value with a case of a enumerated value. Each case has its own independent type, and must be declared separately as a tuple. For example, let’s associate a calorie count and the food eaten with each meal.

We’ll use a class Food for breakfast. So we don’t know what snack we had use an integer only for .Snack

enum Meals{
 case Breakfast(Food)
 case Lunch(Int,String)
 case Dinner(Int,String)
 case Snack(Int)
 }

Getting a value from this is not as easy as rawValue. You have to extract it in a switch statement. It may be in your code somewhere, or as I did here, add it to the enumeration.

 enum Meals{
    case Breakfast(Food)
    case Lunch(Int,String)
    case Dinner(Int,String)
    case Snack(Int)

    func print() -> String{ //return a string describing the meal
        switch self{
        case let Breakfast(food):
            return "Breakfast: " + food.foodName + " \(food.calories) calories"
        case let Lunch(calories, foodName):
            return "Lunch: " + foodName + " \(food.calories) calories"
        case Dinner(calories, foodName):
            return ("Dinner: " + foodName + " calories: \(calories) ")
        case let Snack(calories):
            return ("A tasty snack of  \(calories) calories")
        }

    }
}

Define a class Food to get this code to work:

class Food{
    var calories:Int = 0
    var foodName:String = ""
    init (calories:Int,foodName:String){
        self.calories = calories
        self.foodName = foodName
    }
}

We defined two properties, calories and foodName in our class. We also added an initializer to assign these values directly to a new instance. If you haven’t worked with initializers, I suggest you take a look at my tutorial on classes for more information on this.

After the enum, let test it with this:

var myMeal = Meals.Breakfast(Food(calories:05,foodName:"Grande Brass Pig"))
print (myMeal.print())
myMeal = .Lunch(380,"Grilled Chicken Sandwich")
print (myMeal.print())
myMeal = .Dinner(1130,"Shwarma Fest Platter")
print (myMeal.print())
myMeal = .Snack(280)
print (myMeal.print())

Run the code. You should get this output


Breakfast: Grande Brass Pig 5 calories
Lunch: Grilled Chicken Sandwich 380 calories
Dinner: Shwarma Fest Platter calories: 1130
A tasty snack of 280 calories

Using enums in Real Life

I rarely use associative enums. Most of the things you can do with them might be better done with classes. If I was writing a real calorie counter app like My Fitness Pal, I’d declare a class with an enum as a property like this:

//The enumeration Declared outside of the class it defaults to global for the target.
enum Meals:Int{
    case Breakfast
    case Lunch
    case Dinner
    case Snack 

}
class FoodEntry{
    var foodName:String
    var calories:Int
    var meal:Meals
    init(meal:Meals,foodName:String,calories:Int){
	     self.meal = meal
	     self.foodName = foodName
	     self.calories = calories
    }
}

Make a new sandbox, and add the code above. I could include the enum in the class definition. I generally don’t if I’m defining a class for a model. It’s likely that I will be using boolean expressions at some point to compare things. This

if food.meal == .Breakfast 

is easier to write than

if food.meal == food.Meals.Breakfast

As I did earlier, I did use a constant Int on my enumeration to use it in arrays. This FoodEntry class could also be used in an array for my daily food list in a class for daily food entries:

class DailyFoodIntake{
	var foodList:[FoodEntry] = []
	var totalCalories = 0
	let zeroMealCalories = [0,0,0,0]
	var mealCalories:[Int] = [0,0,0,0]

This class also keeps track of total calories for each meal and for the day. I can set up a few functions like this to use these properties:

func computeCalories(){
	    //recompute daily and meal calorie intake
	    totalCalories = 0
	    mealCalories = zeroMealCalories
	    for food in foodList{
	        totalCalories += food.calories
	        mealCalories[food.meal.rawValue] += food.calories
	    }
	    print("Total Calories today: \(totalCalories)")
	    print("\tBreakfast: \(mealCalories[Meals.Breakfast.rawValue]) calories")
	    print("\tLunch: \(mealCalories[Meals.Lunch.rawValue]) calories")
	    print("\tDinner: \(mealCalories[Meals.Dinner.rawValue]) calories")
	    print("\tSnack: \(mealCalories[Meals.Snack.rawValue]) calories")
	}
	func addFoodForMeal(meal:Meals,foodName:String,calories:Int){
	    let food = FoodEntry(meal:meal,foodName:foodName,calories:calories)
	    foodList += [food]
	    computeCalories()
	}

The addFoodForMeal method adds a food to the array and then calls the computeCalories to recompute the totals for the day. This clears the totals first then adds everything up in a loop. For the meals, we use the rawValue to get an index for the array to total the current value:

mealCalories[food.meal.rawValue] += food.calories

After computing, the totals get printed on the console, with a tab stop indentation. Again, we use the rawValue to get an index.

print("\tBreakfast: \(mealCalories[Meals.Breakfast.rawValue]) calories")
print("\tLunch: \(mealCalories[Meals.Lunch.rawValue]) calories")
print("\tDinner: \(mealCalories[Meals.Dinner.rawValue]) calories")
print("\tSnack: \(mealCalories[Meals.Snack.rawValue]) calories")

Add the following test data

let today = DailyFoodIntake()
today.addFoodForMeal(.Breakfast,foodName:"coffee",calories:5)
today.addFoodForMeal(.Breakfast,foodName:"oatmeal",calories:170)
today.addFoodForMeal(.Lunch,foodName:"turkey dogs",calories: 90)
today.addFoodForMeal(.Dinner,foodName:"Turkey Burger",calories: 340)
today.addFoodForMeal(.Dinner,foodName:"Sweet Potato Fries",calories: 270)
today.addFoodForMeal(.Snack,foodName:"Candy Bar",calories: 280)
today.addFoodForMeal(.Snack,foodName:"Ice Cream mini bar",calories: 180)

Run the code. You’ll get output starting like this:


Total Calories today: 5
Breakfast: 5 calories
Lunch: 0 calories
Dinner: 0 calories
Snack: 0 calories
Total Calories today: 175
Breakfast: 175 calories
Lunch: 0 calories
Dinner: 0 calories
Snack: 0 calories
Total Calories today: 265
Breakfast: 175 calories
Lunch: 90 calories
Dinner: 0 calories
Snack: 0 calories

This is a little too much for our output. Notice we had to write out four print statements. Since you can’t iterate an enum this was necessary. Let’s do a modification to our code to better use our enum. Start by adding a function for a string in the enum

func mealsString()-> String{
        switch self{
            case Breakfast:
                return "breakfast"
            case Lunch:
                return "lunch"
            case Dinner:
                return "dinner"
            case Snack:
                return "snack"
        }

    }

Next, remove the print statements from computeCalories. Change the addFoodForMeal to this

func addFoodForMeal(meal:Meals,foodName:String,calories:Int){
	    let food = FoodEntry(meal:meal,foodName:foodName,calories:calories)
	    foodList += [food]
	    computeCalories()
	    print("Total Calories today: \(totalCalories)")
	    print("\tAdded  \(calories) calories for total of \(mealCalories[meal.rawValue]) \(meal.mealsString()) calories")
	}

In this function, unlike computeCalories, we have a value meal that tells us which meal this is. Instead of printing all the meals, we only print the meal that changed. We use the mealsString function to have a string value for our enum, since we already have an integer one. Run this and you get a different output


Total Calories today: 5
Added 5 calories for total of 5 breakfast calories
Total Calories today: 175
Added 170 calories for total of 175 breakfast calories
Total Calories today: 265
Added 90 calories for total of 90 lunch calories
Total Calories today: 605
Added 340 calories for total of 340 dinner calories
Total Calories today: 875
Added 270 calories for total of 610 dinner calories
Total Calories today: 1155
Added 280 calories for total of 280 snack calories
Total Calories today: 1335
Added 180 calories for total of 460 snack calories

 

Enumeration is the way in swift to deal with options in a function or application easily. In the above example, we used meals, but any small set of constant options works well in one. If you have a changing list or a large one, you might use a different data type like an array or dictionary. As you explore the UIKit API’s you’ll find many uses for enums. You’ll undoubtedly find many more.

The Whole Code

You’ll find the code below for this lesson. Uncomment each part you want to use. You’ll also find it available in the IBM Swift Sandbox here

import Foundation
//Uncomment code to run each iteration.
// find this code at the IBM Swift sandbox at
// swiftlang.ng.bluemix.net/#/repl/057089281afdef410b81365650399f0935617405bf64e7e54806b010e9af6c09
import Foundation
//Uncomment code to run each iteration.
/*
// Iteration one -- Unreadable code
var  caloriesTotal = [0,0,0,0] //Breakfast,Lunch,Dinner,Snack calories
func addCaloriesForMeal(meal:Int,calorie:Int){
//using the rawValue property of enum to get the integer assigned to the enum
	caloriesTotal[meal] = calorie
// using an enum in a boolean expression -- yell at us if snacking
	if meal == 3 {
		print("Try not to snack")
	}
}

func printCaloriesForMeal(meal:Int){
//using the rawValue property of enum to get the integer assigned to the enum
	print ( "Calories for meal \(meal) is \(caloriesTotal[meal])")
// using an enum in a comparison -- Breakfast less than 100 calories
	if (meal == 0) &&  (caloriesTotal[meal] < 100){
		print("Breakfast is the most important meal of the day. Eat more breakfast.")
	}
}

addCaloriesForMeal(0,calorie:50)
addCaloriesForMeal(2,calorie:650)
addCaloriesForMeal(3,calorie:1150)

printCaloriesForMeal(0)
printCaloriesForMeal(3)
*/

/*
// Iteration two add an enumeration and use value and rawValue
enum Meal:Int{
	case Breakfast = 0
	case Lunch = 1
	case Dinner = 2
	case Snack = 3
}

var  caloriesTotal = [0,0,0,0]
func addCaloriesForMeal(meal:Meal,calorie:Int){
//using the rawValue property of enum to get the integer assigned to the enum
	caloriesTotal[meal.rawValue] = calorie
// using an enum in a boolean expression -- yell at us if snacking
	if meal == .Snack {
		print("Try not to snack")
	}
}

func printCaloriesForMeal(meal:Meal){
//using the rawValue property of enum to get the integer assigned to the enum
	print ( "Calories for meal \(meal.rawValue) is \(caloriesTotal[meal.rawValue])")
// using an enum in a comparison -- Breakfast less than 100 calories
	if (meal == .Breakfast) &&  (caloriesTotal[meal.rawValue] < 100){
		print("Breakfast is the most important meal of the day. Eat more breakfast.")
	}
}

addCaloriesForMeal(.Breakfast,calorie:50)
addCaloriesForMeal(.Dinner,calorie:650)
addCaloriesForMeal(.Snack,calorie:1150)

printCaloriesForMeal(.Breakfast)
printCaloriesForMeal(.Snack)
*/

/*
// Iteration three add a switch to make a descriptive string
// enum only has an intial value, values increment from there
enum Meal:Int{
	case Breakfast = 0
	case Lunch
	case Dinner
	case Snack
}

var  caloriesTotal = [0,0,0,0]
func addCaloriesForMeal(meal:Meal,calorie:Int){
	caloriesTotal[meal.rawValue] = calorie
	if meal == .Snack {
		print("Try not to snack")
	}
}

func printCaloriesForMeal(meal:Meal){
//Adding a Switch Statement here to create a string from the enum
    var mealString = ""
    switch meal{
        case .Breakfast:
            mealString = "breakfast"
        case .Lunch:
            mealString = "lunch"
        case .Dinner:
            mealString = "Dinner"
        case .Snack:
            mealString = "Snack"
    }

	print ( "Calories for " + mealString + " is \(caloriesTotal[meal.rawValue])")
	if (meal == .Breakfast) &&  (caloriesTotal[meal.rawValue] < 100){
 		print("Breakfast is the most important meal of the day. Eat more breakfast.")
 	}
 }
 addCaloriesForMeal(.Breakfast,calorie:50)
 addCaloriesForMeal(.Dinner,calorie:650)
 addCaloriesForMeal(.Snack,calorie:1150)
 printCaloriesForMeal(.Breakfast)
 printCaloriesForMeal(.Snack)
*/
/*
 // Iteration four   -- using a function for the case in the enum
 // default is 0 for the first enum, so we don't assign anything
 // I tend to be paranoid and do like iteration 2 if I need raw values.
  enum Meal:Int{
 	case Breakfast = 0
 	case Lunch
 	case Dinner
 	case Snack
 // A function within the enum.
 // Uses self to get the enumeration's value
  func mealName() -> String{
    var mealString = ""
	switch self{
        case .Breakfast:
            mealString = "breakfast"
        case .Lunch:
            mealString = "lunch"
        case .Dinner:
            mealString = "Dinner"
        case .Snack:
            mealString = "snack"
    }
    return mealString
	}
}

var  caloriesTotal = [0,0,0,0]
func addCaloriesForMeal(meal:Meal,calorie:Int){
	caloriesTotal[meal.rawValue] = calorie
	if meal == .Snack {
		print("Try not to snack")
	}
}

func printCaloriesForMeal(meal:Meal){
    /*var mealString = ""
    switch meal{
        case .Breakfast:
            mealString = "breakfast"
        case .Lunch:
            mealString = "lunch"
        case .Dinner:
            mealString = "Dinner"
        case .Snack:
            mealString = "Snack"
    }*/

	print ( "Calories for " + meal.mealName() + " is \(caloriesTotal[meal.rawValue])")
	if (meal == .Breakfast) && (caloriesTotal[meal.rawValue]< 100){
 		print("Breakfast is the most important meal of the day. Eat more breakfast.")
 	}
 }
 addCaloriesForMeal(.Breakfast,calorie:50)
 addCaloriesForMeal(.Dinner,calorie:650)
 addCaloriesForMeal(.Snack,calorie:1150)
 printCaloriesForMeal(.Breakfast)
 printCaloriesForMeal(.Snack)
 */
 /*
 // Use of other types
  //  any Int Float, Double, or String Works here.
  enum Meals:String{
     case Breakfast = "breakfast"
     case Lunch = "lunch"
     case Dinner = "dinner"
     case Snack = "snack"
 }
 print(Meals.Breakfast.rawValue)
 */
 /*
 // Basic enum to make a discrete set of values
 enum Meals{
    case Breakfast
    case Lunch
    case Dinner
    case Snack
}
print (Meals.Breakfast)
*/
/*
 //If you want to do this in a shorter version you can do this:
enum Meals{
   case Breakfast,Lunch,Dinner,Snack
}
print (Meals.Breakfast)
*/
/*
 //The short version works for Int enums with default values
enum Meals:Int{
   case Breakfast,Lunch,Dinner,Snack
}
*/
/*
// Associative enums
  class Food{
     var calories:Int = 0
     var foodName:String = ""
     init (calories:Int,foodName:String){
         self.calories = calories
         self.foodName = foodName
     }
 }
 enum Meals{
     case Breakfast(Food)
     case Lunch(Int,String)
     case Dinner(Int,String)
     case Snack(Int)
     func print() -> String{
        switch self{
        case let Breakfast(food):
            return "Breakfast: " + food.foodName + " \(food.calories) calories"
        case let Lunch(calories, foodName ):
            return "Lunch: " + foodName + " \(calories) calories"
        case let Dinner(calories, foodName):
            return ("Dinner: " + foodName + " calories: \(calories) ")
        case let Snack(calories):
            return ("A tasty snack of  \(calories) calories")
        }

    }
}

var myMeal = Meals.Breakfast(Food(calories:05,foodName:"Grande Brass Pig"))
print (myMeal.print())
myMeal = .Lunch(380,"Grilled Chicken Sandwich")
print (myMeal.print())
myMeal = .Dinner(1130,"Shwarma Fest Platter")
print (myMeal.print())
myMeal = .Snack(280)
print (myMeal.print())
*/
/* 

// a realistic use of enumeration in a calorie counter.
enum Meals:Int{
    case Breakfast
    case Lunch
    case Dinner
    case Snack 

}
class FoodEntry{
    var foodName:String
    var calories:Int
    var meal:Meals
    init(meal:Meals,foodName:String,calories:Int){
	     self.meal = meal
	     self.foodName = foodName
	     self.calories = calories
    }
}

class DailyFoodIntake{
	var foodList:[FoodEntry] = []
	var totalCalories = 0
	let zeroMealCalories = [0,0,0,0]
	var mealCalories:[Int] = [0,0,0,0]

	func computeCalories(){
	    //recompute daily and meal calorie intake
	    totalCalories = 0
	    mealCalories = zeroMealCalories
	    for food in foodList{
	        totalCalories += food.calories
	        mealCalories[food.meal.rawValue] += food.calories
	    }
	    print("Total Calories today: \(totalCalories)")
	    print("\tBreakfast: \(mealCalories[Meals.Breakfast.rawValue]) calories")
	    print("\tLunch: \(mealCalories[Meals.Lunch.rawValue]) calories")
	    print("\tDinner: \(mealCalories[Meals.Dinner.rawValue]) calories")
	    print("\tSnack: \(mealCalories[Meals.Snack.rawValue]) calories")
	}
	func addFoodForMeal(meal:Meals,foodName:String,calories:Int){
	    let food = FoodEntry(meal:meal,foodName:foodName,calories:calories)
	    foodList += [food]
	    computeCalories()
	}
}

let today = DailyFoodIntake()
today.addFoodForMeal(.Breakfast,foodName:"coffee",calories:5)
today.addFoodForMeal(.Breakfast,foodName:"oatmeal",calories:170)
today.addFoodForMeal(.Lunch,foodName:"turkey dogs",calories: 90)
today.addFoodForMeal(.Dinner,foodName:"Turkey Burger",calories: 340)
today.addFoodForMeal(.Dinner,foodName:"Sweet Potato Fries",calories: 270)
today.addFoodForMeal(.Snack,foodName:"Candy Bar",calories: 280)
today.addFoodForMeal(.Snack,foodName:"Ice Cream mini bar",calories: 180)
*/

/*
// changes to the output to summarize the meal eaten and daily totals
enum Meals:Int{
    case Breakfast
    case Lunch
    case Dinner
    case Snack 

    func mealsString()-> String{
        switch self{
            case Breakfast:
                return "breakfast"
            case Lunch:
                return "lunch"
            case Dinner:
                return "dinner"
            case Snack:
                return "snack"
        }

    }
}
class FoodEntry{
    var foodName:String
    var calories:Int
    var meal:Meals
    init(meal:Meals,foodName:String,calories:Int){
	     self.meal = meal
	     self.foodName = foodName
	     self.calories = calories
    }
}

class DailyFoodIntake{
	var foodList:[FoodEntry] = []
	var totalCalories = 0
	let zeroMealCalories = [0,0,0,0]
	var mealCalories:[Int] = [0,0,0,0]

	func computeCalories(){
	    //recompute daily and meal calorie intake
	    totalCalories = 0
	    mealCalories = zeroMealCalories
	    for food in foodList{
	        totalCalories += food.calories
	        mealCalories[food.meal.rawValue] += food.calories
	    }
	}

	func addFoodForMeal(meal:Meals,foodName:String,calories:Int){
	    let food = FoodEntry(meal:meal,foodName:foodName,calories:calories)
	    foodList += [food]
	    computeCalories()
	    print("Total Calories today: \(totalCalories)")
	    print("\tAdded  \(calories) calories for total of \(mealCalories[meal.rawValue]) \(meal.mealsString()) calories")

	}
}

let today = DailyFoodIntake()
today.addFoodForMeal(.Breakfast,foodName:"coffee",calories:5)
today.addFoodForMeal(.Breakfast,foodName:"oatmeal",calories:170)
today.addFoodForMeal(.Lunch,foodName:"turkey dogs",calories: 90)
today.addFoodForMeal(.Dinner,foodName:"Turkey Burger",calories: 340)
today.addFoodForMeal(.Dinner,foodName:"Sweet Potato Fries",calories: 270)
today.addFoodForMeal(.Snack,foodName:"Candy Bar",calories: 280)
today.addFoodForMeal(.Snack,foodName:"Ice Cream mini bar",calories: 180)

*/

How to Use Custom Table Cells in a UITableView

While very versatile, there’s some point where every developer finds table views lacking something: the cell format is too limited.  Apps like Facebook, Twitter, and Instagram don’t use simple table views. They use  custom table cell formats.  In this lesson, we’ll start learning how to make and use custom table view cells.

Set Up the Project.

We’ll start with a new project. Create a new project named DessertTableView, with a Universal device and Swift as the language.

We’ll be using some images in this app. You can download the images here. Unzip the images. Open the Assets.xcassets folder in the navigator. Select all the image files and drag them into the asset library.

2015-12-02_06-22-41

You have all three images available to the project.

2015-12-02_06-24-35

Setting Up the Storyboard

Go to the storyboard. Delete the view controller. Drag a new Table View Controller to the storyboard. In the attribute inspector select Is Initial Controller. Open up your document outline if not already open. In the document outline, select the table view.

2015-12-02_06-26-17

In the attributes use Prototype cells and change the count to 2.

2015-12-02_06-26-34

 

You can  have more than one format for cells. You’ll find in the outline two table view cells.

Select the top one and use  a Subtitle cell style named cell as the identifier.

2015-12-03_07-42-19

Change the background color to #FFEEEE in the color picker

2015-12-04_06-02-16

Select the bottom one and make it a Right Detail cell named options cell as the identifier. Change the Background color to #EEFFFF.

Set Up the Basic Table View

Now go to the ViewController.swift file. Change the class name to this:

class DessertTableViewController: UITableViewController {

As I’ve mentioned in other lessons about table views, I find adding everything from scratch a lot less confusing and cleaner than the UITableViewController class template.

Set Up the Model

Before we add the data source methods for the table view, we need a model. Add this dictionary and array to the class:

var  menu:[String:String] = [
    "Ice Cream Cone": "Ice Cream",
    "Ice Cream Sundae": "Ice Cream",
    "Apple Pie": "Pie",
    "Cherry Pie": "Pie",
    "Coconut Cream": "Pie",
    "Tiramisu": "Cake",
    "Chocolate Chip Cookie": "Cookie",
    "7-Layer Cake": "Cake",
    "Boston Cream Doughnut": "Doughnut",
    "Cruller": "Doughnut",
    "Long John": "Doughnut",
    "Blueberry Muffin": "Cake",
    "Vanilla Cupcake": "Cake",
    "Shake": "Drink",
    "Malted": "Drink",
    "Root Beer Float": "Drink"]
var dessertList = [String]()

We’ll populate the array with the keys to the dictionary. We do that in viewDidLoad like this:

override func viewDidLoad() {
   super.viewDidLoad()
   dessertList = [String](menu.keys)
}

Remember that dictionaries are unordered collections. Arrays are ordered collections. We need an order collection for a table view to work, which is what we will use dessertList for. Your dessert list will most likely not be in  the same order as mine since we take the unordered collection and make it ordered. Your table might then look different.

Data Source and Delegates for the Table View

Next, declare our data source methods.Add this to the bottom of your class:

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

We’ll have one section, and dessertList.count entries in our list. Add this to the class:

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

Our last data source method populates the cells. This will be where we will do most of our work. For our first iteration we’ll alternate between the two types of views we have. Add the following code.

//iteration 1 
override func tableView(
    tableView: UITableView,
    cellForRowAtIndexPath indexPath: NSIndexPath
) -> UITableViewCell {
    let row = indexPath.row
    let title = dessertList[row]
    let detail = menu[title]!
        
    if row % 2 == 0 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", 
            forIndexPath: indexPath)
        cell.textLabel?.text = title
        cell.detailTextLabel?.text = detail
        return cell
    }else{
        let cell = tableView.dequeueReusableCellWithIdentifier("options cell", 
            forIndexPath: indexPath)
        cell.textLabel?.text = title
        cell.detailTextLabel?.text = detail
        return cell
    }
}

The expression row % 2 alternates between 1 and 0. When 0, we dequeue the cell table view cell and display that. Otherwise we dequeue the options cell table view cell and use that.
Go back to the storyboard. Select the table view controller and in the identity inspector set the class to DessertTableViewController. Build and run. we now have alternating cells.

2015-12-02_07-04-09

Using Logical Cells

While this might be pretty, in other table view lessons we’ve done this.  In practice, we will have circumstances that will use one cell instead of another based on our data. For our demo, we have  ice cream, shakes, malts, and pies we specify ice cream flavors for in an options modal view. Those will need the options cell to select the ice cream.

Go to the storyboard and add a new view controller. Add three buttons for Vanilla, Chocolate and Strawberry. Make the background #88FF88 and the button backgrounds #88FFFF with  black text. Select all three buttons and tap the Stack view button stack view button. Set the stack view like this.

2015-12-02_07-19-30

Using Auto Layout, pin the stack view 20 points  up, 0 left and 0 right, updating the items of new constraints. You’ll get a scene like this when you are done.

2015-12-02_07-21-03

Control-drag from the options cell to the new scene.  Select a Present Modally segue for Selection.   Select the segue. In the attribute inspector,  set the Presentation to Form Sheet. Press Command-N on the keyboard to make a new file. Create a new Cocoa Touch Class file named OptionsViewController, Subclassing UIViewController. Add this action to the file that appears

@IBAction func selectedFlavor(sender: UIButton) {
    dismissViewControllerAnimated(true, completion: nil)
}  

Go back to the storyboard. Select the options scene’s view controller icon. In the identity inspector, set the class to OptionsViewController. Open the assistant editor.  Select the Vanilla button. Control drag from the Vanilla button to the selectedFlavor code until the code highlights. Do the same for Chocolate and Strawberry.

Now that we set up our storyboard go back to the DessertTableView class. Add this to cellForRowAtIndexpath:

let hasOptions =
    (detail == "Pie") ||
    (detail == "Ice Cream") ||
    (detail == "Drink")

Change row % 2 == 0 to hasOptions. Now for ice cream, pie and dessert drinks, we can pick a flavor of ice cream when we select the item, while other items will have no option selections.

Build and run. We see some items with selections and others without

2015-12-02_07-35-57

Select Apple Pie and it asks us what ice cream we want.

2015-12-02_07-36-02

Adding a Custom View Cell

The most powerful type of cell is a custom cell. For the non-options menu choices, we’ll add a photo to the menu selection. While we can do this in a standard view with the image property, it will only show up to the left of all the text. We’ll put our photo with the title above and the detail next to it. With stack views, this becomes rather easy to lay out.

Layout with Embedded Stack Views

Select cell in the document outline. In the attribute inspector, change the style to Custom. The title and detail disappear. Give ourselves a bit more space. Change to the size inspector for cell. Set a Row Height of 200. Custom will check on.

2015-12-04_05-40-36

This setting is meaningless for the actual app. We set the row height with the estimatedHeightForRowAtIndexPath delegate. It just helps with layout.

Drag two labels and an image view into the cell. Make one label read Title and the other Detail. Set the text color of both labels to #DDDDEE.

Make the Title label font System 24 points. Change the background to #664433. Arrange everything to look like this:

2015-12-03_05-41-39

Select the image view. In the pin menu, set the width to 100 and check on Aspect Ratio. In the size inspector, edit the aspect ratio to be 1:1.

2015-12-03_06-05-37

This sizes our image to 100 points square and prevents the stack view from getting confused.

Select both the Image view and the Detail label. Make a stack view by clicking the stack view button stack view button. Change the attributes of the stack view to this:

2015-12-03_05-44-56

Select the image view and set the Image Mode to Aspect Fit in the attributes inspector. Change to the size inspector, and change the image view’s Content Hugging Priority to 750. This is necessary for the stack view to work right. (For a discussion why, see my book Practical Auto Layout for Xcode 7) Select the Detail button and change the Content Hugging Priority to 250.

Select the stack view and the Title label in the document outline. Click the stack view button stack view button again, this time making a vertical stack view. Use these settings for this stack view.

2015-12-03_05-55-14

Click on the auto layout pin button. Turn on all four constraints by clicking the I-beams and add the constraints as is. Go to the size inspector for the stack view and edit the constraints so all have a zero constant. Your constraints should look like this:

2015-12-03_06-19-03

For an explanation of using stack views, see the stack view lesson. Your layout should look like this:

2015-12-03_13-51-57

Set Up the Cell Class

Press Command-N and make a new Cocoa Touch Class named CustomTableViewCell. Subclass UITableViewCell. In the class that appears add the three following outlets

@IBOutlet weak var customTitle: UILabel!
@IBOutlet weak var customImage: UIImageView!
@IBOutlet weak var customDetail: UILabel!

Go back to the Storyboard. Select cell and in the identity inspector change its class to CustomTableCell. Open the assistant editor. Xcode does not immediately recognize that you want to add outlets to a cell and will insist on adding outlets to the table view controller. You have to force it. In the assistant editor select Manual, and drill-down the folders to select CustomTableViewCell.swift. Drag from the circles to the appropriate labels and the image view to connect the outlets.

Use the Custom Cell in the Table

Close the assistant editor. Go to the DessertTableViewController class. Find the tableview:cellForRowAtIndexPath: method. We’ll change the code for setting up the cell with identifier cell. This is relatively simple. We’ve subclassed UITableViewCell, so we need to downcast the value of dequeueReusableCellWithIdentifier with CustomTableViewCell. We’ll then have access to the custom cell’s properties and set them accordingly. Change that part of the method to this:

}else{
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", 
         forIndexPath: indexPath)
         as! CustomTableViewCell
    cell.customTitle.text = title
    cell.customDetail.text = detail
    cell.customImage.image = UIImage(named: detail)
    return cell
}

Build and run. It almost works. We get two unexpected results. The first is a squashed image in the table view and a missing title:

2015-12-03_06-46-34

The second is Xcode issuing a constraint error.

DessertTableView[] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want.

Setting the Height of a Table Cell

The cause is the same. Even though we set a height of 200 points in the storyboard, this gets ignored at run time. The table view uses its default height of 44 points, which is too small for our custom view. Since we have constraints that need more than that much space, we get the conflicting constraint error.  We need to tell the table view what height we want. There is a delegate for just that. Add another delegate to the code:

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

the estimatedHeightForRowAtIndexPath delegate sets the estimated height of the rows. If a row is smaller than this number, it will be this value. If the system finds that a cell is bigger than this height, it automatically adjusts the height. Build and run. You will find the app now works.

2015-12-03_06-53-42

Custom cells allow for many special cases of table views you could not get other wise. Many apps are nothing more than such custom cells. You don’t need to have only one type of cell either. You can make several for different cases. Prior to Xcode 7, the cells needed to be set up completely with auto layout. Now we can use stack views to speed up the layout process.

For more information on stack views, check out the introduction to stack views.

The Whole Code

DessertTableViewController

//
//  ViewController.swift
//  DessertTableViewController
//
//  Created by Steven Lipton on 12/2/15.
//  Copyright © 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class DessertTableViewController: UITableViewController {
    var  menu:[String:String] = [
        "Ice Cream Cone": "Ice Cream",
        "Ice Cream Sundae": "Ice Cream",
        "Apple Pie": "Pie",
        "Cherry Pie": "Pie",
        "Coconut Cream": "Pie",
        "Tiramisu": "Cake",
        "Chocolate Chip Cookie": "Cookie",
        "7-Layer Cake": "Cake",
        "Boston Cream Doughnut": "Doughnut",
        "Cruller": "Doughnut",
        "Long John": "Doughnut",
        "Blueberry Muffin": "Cake",
        "Vanilla Cupcake": "Cake",
        "Shake": "Drink",
        "Malted": "Drink",
        "Root Beer Float": "Drink"]
    var dessertList = [String]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        dessertList = [String](menu.keys)
    }

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

    //MARK: Table Delegates and Data Sources
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dessertList.count
    }
    /*
    //Iteration 1
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let row = indexPath.row
        let title = dessertList[row]
        let detail = menu[title]!
        
        if row % 2 == 0 {
            let cell = tableView.dequeueReusableCellWithIdentifier("options cell", forIndexPath: indexPath)
            cell.textLabel?.text = title
            cell.detailTextLabel?.text = detail
            return cell
        }else{
            let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
            cell.textLabel?.text = title
            cell.detailTextLabel?.text = detail
            return cell
        }
    }*/
    
    /*
    //Iteration 2 -- logically selecting a cell
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let row = indexPath.row
        let title = dessertList[row]
        let detail = menu[title]!
        let hasOptions =
            (detail == "Pie") ||
            (detail == "Ice Cream") ||
            (detail == "Drink")
        if hasOptions {
            let cell = tableView.dequeueReusableCellWithIdentifier("options cell", forIndexPath: indexPath)
            cell.textLabel?.text = title
            cell.detailTextLabel?.text = detail
            return cell
        }else{
            let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
            cell.textLabel?.text = title
            cell.detailTextLabel?.text = detail
            return cell
        }
    }
    */
    //Iteration 3 -- custom cell
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let row = indexPath.row
        let title = dessertList[row]
        let detail = menu[title]!
        let hasOptions =
        (detail == "Pie") ||
            (detail == "Ice Cream") ||
            (detail == "Drink")
        if hasOptions {
            let cell = tableView.dequeueReusableCellWithIdentifier("options cell", forIndexPath: indexPath)
            cell.textLabel?.text = title
            cell.detailTextLabel?.text = detail
            return cell
        }else{
            let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomTableViewCell
            cell.customTitle.text = title
            cell.customDetail.text = detail
            cell.customImage.image = UIImage(named: detail)
            return cell
        }
    }

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

 

    
}


CustomTableViewCell

//
//  CustomTableViewCell.swift
//  DessertTableView
//
//  Created by Steven Lipton on 12/2/15.
//  Copyright © 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class CustomTableViewCell: UITableViewCell {
    @IBOutlet weak var customTitle: UILabel!
    @IBOutlet weak var customImage: UIImageView!
    @IBOutlet weak var customDetail: UILabel!

    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
    }

}
 

OptionsViewController

//
//  OptionsViewController.swift
//  DessertTableView
//
//  Created by Steven Lipton on 12/2/15.
//  Copyright © 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class OptionsViewController: UIViewController {

    @IBAction func selectedFlavor(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}