Some 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.
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.
Then pin the Order Pie button 30 up, 30 left and 30 right, again updating the frames.
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:
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:
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.
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:
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.
If you have a constraint missing, add it from the pin menu. The Preview has the buttons in the right place but sized incorrectly:
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.
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.
Select the Submit Result Button and change the multiplier to 1:2
Again for all views in view, select Update Frames. We have a completed layout that looks pretty good.
Resolving Six Warnings
Unfortunately, we get six warnings. Going to the error panel you will find them:
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:
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.
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
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) } }
Leave a Reply