Swift Swift Tutorials: Calculator Style buttons in an Auto Layout Xib

frame 2015-01-21 17.18.00Some people want calculator-type buttons. It would be nice to have such buttons for say, an order entry app. In this lesson we’ll put together a set of buttons to make a layout for an order entry system. Along the way I’ll show how to use a Xib as a modal view in Swift. This requires some knowledge of auto layout. If you haven’t used Auto layout before I’d suggest reviewing Beginning Auto Layout first.

Set Up the Starting View

Start by making a new project with Command-Shift-N in Xcode. Make a single view project and name it PieOrderEntry, make the device Universal. Choose Swift for the language. Set your storyboard up like this with one label and one button.

Screenshot 2015-01-22 14.32.50

Click the label. In the properties inspector center align the text. Then using auto layout, pin the label 50 up, 30 left and 30 right. Update the frame with new constraints.

Screenshot 2015-01-22 14.34.59

Then pin the Order Pie button 30 up, 30 left and 30 right, again updating the frames.

Screenshot 2015-01-22 14.35.45

Go into assistant editor, select Automatic, and click the view controller in the storyboard. Control-drag the label to the view controller. Name the outlet statusLabel . Control-drag the button and make it a target action named myModalButton . Remove the rest of the methods in the view controller class so your view controller looks like this:

class ViewController: UIViewController {
    @IBOutlet weak var statusLabel: UILabel!
//MARK: - Target actions
    @IBAction func myModalButton(sender: UIButton) {
    }
  //MARK: - Delegate methods
}

Make a View Controller With a Xib

There is only one check box between using storyboard and a xib. Press Command-N to get a new file, and under iOS click a new Cocoa Touch Class . Click Next and fill out the dialog box like this with a new subclass of UIViewController named MyModalVC:

Screenshot 2015-01-22 14.43.08

Be sure to check on the Also create XIB file selection. This will create and associate a xib file with this view controller. If you have never seen a xib, it looks a lot like a storyboard with only one scene. You use it exactly the same way — even auto layout. The difference is the Xib is independent of the storyboard. You can take the MyModalVC.swift and MyModalVC.xib file from here, and import them into another project ready for use.

Layout for the Xib

After making the view controller, the xib should appear.

Once loaded, switch to the Storyboard. Add one label and seven buttons. In the properties inspector, make the label text read What is my pie? and center align the text. Position it towards the top of the view.  On one button, label it Submit Result with a red background and white text. Drag the Submit Result button towards the bottom. Make the rest of the buttons a blue color with white lettering. Title the buttons Pizza,Chicken Pot,Shepherd’s,Apple,Chocolate,and Blueberry. Arrange the buttons roughly like this:

Screenshot 2015-01-21 05.12.07

We will use auto layout pins to do most of the work. We need to get the bottom and top constrained first to always be the nearest neighbor.
Select the label. In the properties inspector,change the background to white. Pin the label 50 from the top and 0 from left and right. Update the frame.
Change the view’s background to black. It will help us see everything moving around.

Screenshot 2015-01-21 05.38.31

For the button titled Submit Result, Pin it 20 from the bottom and 0 from the left and right. Update the frame.

We now can start working on the buttons. Like Chapter 3, we will start with one button, and pin everything around it together. Because everything will get messy we will not update frames until we are done with this process.

Close the left hand navigator frame and open the assistant editor to the preview mode. The default iPhone 4 inch will do fine for checking our work as we go. Your workspace should look like this:

Screenshot 2015-01-21 05.47.02

We will use a 20 point gutter between buttons. Select the Pizza button in the upper left. Pin the button 20 up, 0 left, 20 down, and 20 right.
Select the Apple button. Since we already have a relationship to the Pizza button, we don’t need a top pin. Pin the Apple button 0 left, 20 right, and 20 down.
Select the Chocolate button. Pin the button down 20 and right 20.
Select the Blueberry button. Pin the button down 20 and right 0.
Select the Shepherd’s button. Pin the button up 20, left 20, right 0, and down 20
Select the Chicken Pot button. Pin up 20 and down 20.
You now have all the buttons pinned in all four directions. Clicking on each button and make sure you see four constraints, which most likely will be misplaced like this.

Screenshot 2015-01-21 06.02.17

If you have a constraint missing, add it from the pin menu. The Preview has the buttons in the right place but sized incorrectly:

Screenshot 2015-01-21 06.06.58

We have not set any size information. Select the Pizza Button. Control-Drag from the Pizza Button to the Label. Select Equal Heights.
Control drag from the Pizza Button to the Submit Result button. Select Equal Heights.
Control-Drag from the Pizza button to the Apple Button. Shift Select Equal Width and Equal Heights and press return. Repeat for the other four blue buttons, always dragging from Pizza to the Chocolate, Blueberry,Meat and Chicken Pot.

In the resolver, for All Views in View, select Update Frames.

Screenshot 2015-01-21 06.16.39

The label and Submit Result button is a bit too big. Select the label. Go to the Size inspector and select Edit for the Equal Height to Pizza. Change the multiplier to 2:3.

Screenshot 2015-01-21 06.21.00

Select the Submit Result Button and change the multiplier to 1:2

Screenshot 2015-01-21 06.23.26

Again for all views in view, select Update Frames. We have a completed layout that looks pretty good.

Screenshot 2015-01-21 06.28.38

Resolving Six Warnings

Unfortunately, we get six warnings. Going to the error panel you will find them:

Screenshot 2015-01-21 06.41.24

Reading the error we note that each button is off by one. Annoyingly you can resolve all but the pizza. If your resolve the pizza misplacement, all the errors come back.
All of the errors are height errors. This is a rounding error, and its cause is the multiplier for the label which is 2:3. As a repeating decimal, it is causing rounding errors between all the views. One easiest answer is to change the multiplier from 2:3 to 1:2. The error goes away.
In the preview, both Landscape and Portrait work:

Screenshot 2015-01-21 06.52.39  Screenshot 2015-01-21 06.52.51

Connect the Labels and Buttons

Set your workspace up for wiring up the views. Open the assistant editor if not already open. Select Automatic for the assistant editor. Hide the Properties or Size Inspector to give yourself some room.

In the assistant editor, remove the methods already in the class, including the commented out prepareForsegue . Control drag the What is my Pie Label to make an @IBOutlet named pieLabel . Control drag the Submit Result button and make an @IBAction for a UIButton named submitResult . Control drag the Pizza button and make an action for a UIButton named pieSelectionButton . Your class should look like this:

class MyModalVC: UIViewController {
  @IBOutlet weak var pieLabel: UILabel!

  @IBAction func pieSelectionButton(sender: UIButton) {
  }

  @IBAction func submitResult(sender: UIButton) {
  }
}

From the circle to the left of the pieSelectionButton method, click then drag to the other five blue pie buttons and release to connect them to the action. When connected, they will highlight as you hover over the circle.
Add the following property above the @IBOutlet:

var pieString = "No Pie"

Fill in our two methods so the entire class looks like this.

import UIKit
class MyModalVC: UIViewController {
   var pieString = "No Pie"
   @IBOutlet weak var pieLabel: UILabel!

    @IBAction func pieSelectionButton(sender: UIButton) {
        pieString = sender.titleLabel!.text!						//7
        pieLabel.text = pieString + " Pie"							//8
    }

    @IBAction func submitResult(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)		//12
    }
}

Line 7 takes the tile and places it in a string property pieString. Note the titleLabel property is an optional UILabel and the text property of UILabel is also optional. We unwrap them before concatenating to the string” Pie” in line 8. Line 12 dismisses the modal view, though we will change this shortly.
Open up the navigator. and close the assistant editor. Go back to the viewController.swift file. Just under the class declaration, add the following:

let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil)

There is an initializer for UIVIewController called init(nibName:bundle:) . This line creates an instance of the view controller MyModalVC that uses the xib specified as a string for nibName: We can specify a bundle location if necessary. If we leave the bundle nil , the compiler will search for it in our code bundle.
We need to set a transition and present the modal view controller. Change the code for myModalButton() to the following

  @IBAction func myModalButton(sender: UIButton) {
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: true, completion: nil)
  }

In line 2, We set the transition style to .PartialCurl to simulate a waiter’s order pad. We then present the view controller in line 3. This is enough code to run the app. Build and Run.

      frame 2015-01-21 17.18.00      frame 2015-01-21 17.18.30

Add a Delegate to the Modal View

The modal view currently does not give the root view controller any data back. For that, we will need a delegate. Delegates in modal views are a bit simpler than in navigation controllers. We still start with a protocol. Go to the MyModalVC.swift code and add this above the class declaration:

protocol MyModalDelegate{
  func myModalDidFinish(controller:MyModalVC, pie:String)
}

In the MyModalVC class add a delegate property:

var delegate:MyModalDelegate! = nil
Change the method submitResult() to use the delegate:
@IBAction func submitResult(sender: UIButton) {
    //dismissViewControllerAnimated(true, completion: nil)
    delegate.myModalDidFinish(self, pie: pieString)
}

Now head over to ViewController.swift to adopt the protocol:

class ViewController: UIViewController,MyModalDelegate {

Right on cue, Xcode complains we have not implemented the delegate. Add the following code to the ViewController class:

//MARK: - Delegate methods
func myModalDidFinish(controller: MyModalVC, pie: String) {
    statusLabel.text = “Order ” + pie + " pie"
    controller.dismissViewControllerAnimated(true, completion: nil)
}

We put our result in the label, then dismiss the modal view from the delegate method. Now assign the delegate to this view controller by changing myModalButton :

@IBAction func myModalButton(sender: UIButton) {
    pieVC.delegate = self
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: true, completion: nil)
}

While storyboard-based views need to place the delegate declaration in a prepareForSegue method, here we directly send it to the view controller. We now should have a working delegate. Build and run

frame 2015-01-21 17.19.00      frame 2015-01-21 17.19.30

And it works!

The Whole Code

My ModalVC.swift

//
//  MyModalVC.swift
//
//
//  Created by Steven Lipton on 1/20/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit
protocol MyModalDelegate{
    func myModalDidFinish(controller:MyModalVC, pie:String)
}
class MyModalVC: UIViewController {
    var pieString = "No Pie"
    var delegate:MyModalDelegate! = nil
    @IBOutlet weak var pieLabel: UILabel!

    @IBAction func pieSelectionButton(sender: UIButton) {
        pieString = sender.titleLabel!.text!						//7
        pieLabel.text = pieString + " Pie"							//8
    }
    @IBAction func submitResult(sender: UIButton) {
        //dismissViewControllerAnimated(true, completion: nil)		//12
        delegate.myModalDidFinish(self, pie: pieString)
    }

}

ViewController.swift

//
//  ViewController.swift
//
//
//  Created by Steven Lipton on 1/20/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController,MyModalDelegate {
    let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil)
    @IBOutlet weak var statusLabel: UILabel!

    @IBAction func myModalButton(sender: UIButton) {
        pieVC.delegate = self
        pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
        presentViewController(pieVC, animated: true, completion: nil)
    }

    //MARK - Delegate Methods
   func myModalDidFinish(controller: MyModalVC, pie: String) {
        statusLabel.text = "Order " + pie + " pie"
        controller.dismissViewControllerAnimated(true, completion: nil)
   }
}

3 Replies to “Swift Swift Tutorials: Calculator Style buttons in an Auto Layout Xib”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s