In the last post, I showed how to create modal views and popovers on an iPad . That lesson was missing two critical things for modal views, which we will cover in this one. We will learn how to add a .xib file to a view controller. Then we will add a delegate to return data from the modal view controller.
Setting up the New Project
In Xcode 6, go to File>New>Project… or hit Command-Shift-N. Create a Single-View project called SwiftModalDemo, set the language to Swift. We will be working with modal view on iPhone, so set the device to iPhone, then save the project to a place you’d like.
Set your storyboard up like this with one label and one button. For speed and since this is only a demo, I turned off Auto Layout for quick and dirty layout.
Go into assistant editor and 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
One very old part of the cocoa SDK is the .nib
file often know as nibs. Nibs have since been renamed the .xib
file, or xibs when the file format changed to XML. This is a single scene not connected to any storyboard. Many old-school developers swear by them and only use xibs for their work. For most developers with a lot of encouragement from Apple, storyboards with segues have replaced them. Xibs do have two great uses though. When you need a view in several places, say as a special input device or settings page xibs work great as a modal view. Some objects have views which can customized. The UIImagePickerController
property cameraOverlayView
in camera mode for example has such a view. The xib makes programming that custom view a lot easier than coding your own controls.
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:
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. Add at least one label and three buttons. Make the label text read What is my pie? Make one button title Submit Result. For the titles on the rest of the buttons, put types of pies. I went wild and added several pies to make a view like this:
Open the assistant editor again, if not already open. For simplicity, remove the methods already in the class, including the commented out prepareForsegue
. Control drag the What is my pie Label to make an outlet named pieLabel. Control drag one button and make an @IBAction for a UIButton
named submitResult. Control drag one of the remaining buttons with a pie title and make a 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 each button and release to connect them to the action. When connected they will highlight. Now fill in our two methods like this.
import UIKit class MyModalVC: UIViewController { @IBOutlet weak var pieLabel: UILabel! @IBAction func pieSelectionButton(sender: UIButton) { pieLabel.text = sender.titleLabel?.text } @IBAction func submitResult(sender: UIButton) { dismissViewControllerAnimated(true, completion: nil) } }
Line 6 sets the label to the title of the button. Line 10 dismisses the modal view, though we will change this shortly. 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.
Now 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 modal view pieVC
on the screen over our current 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 does not give the root view controller any data back. For that we will need a delegate. If you are not familiar in delegates, I’d suggest reading Why Do we need delegates? to understand what is going on. Delegates in modal views are a bit simpler than in navigation controllers, but have the same parts. 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: pieLabel.text!) }
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 = pie + " pie" controller.dismissViewControllerAnimated(true, completion: nil) }
With this code, 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! There are still a few things you can do with a modal on a iPad you can’t with the iPhone. We’ll discuss those and more about popovers in the next session.
The Whole Code
Here is all the code for implementing a modal
</pre></pre> <h2>MyModalVC.swift</h2> <pre> <pre> // //MyModalVC.swift //SwiftXibModalDemo // // Created by Steven Lipton on 9/3/14. // Copyright (c) 2014 MakeAppPie.Com. All rights reserved. // importUIKit protocolMyModalDelegate{funcmyModalDidFinish(controller:MyModalVC, pie:String) } class MyModalVC:UIViewController{ var delegate:MyModalDelegate! = nil @IBOutlet weak varpieLabel:UILabel! @IBActionfuncpieSelectionButton(sender:UIButton) { pieLabel.text = sender.titleLabel?.text } @IBActionfuncsubmitResult(sender: UIButton) { //dismissViewControllerAnimated(true, completion: nil) delegate.myModalDidFinish(self, pie: pieLabel.text!) } }</pre> <h2>ViewController.swift</h2> <pre> // // ViewController.swift // SwiftModalDemo // // Created by Steven Lipton on 9/1/14. // Copyright (c) 2014 MakeAppPie.Com. All rights reserved. // import UIKit class ViewController: UIViewController,MyModalDelegate { let pieVC = MyModalVC(nibName: "MyModalVC", bundle: nil) @IBOutlet weak var statusLabel: UILabel! //MARK: - Target actions @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 = pie + " pie" controller.dismissViewControllerAnimated(true, completion: nil) } }
Leave a Reply