Make App Pie

Training for Developers and Artists

Swift Swift: Using Xibs and Delegates with Modal Views

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.

Screenshot 2014-09-04 06.47.36

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:
Screenshot 2014-09-03 06.27.27

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:

Screenshot 2014-09-03 17.53.12

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.

modalView 1

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:

modalView 2

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)
}

}

9 responses to “Swift Swift: Using Xibs and Delegates with Modal Views”

  1. Hello,
    What is “presentViewController” in “myModalButton” function?
    Thanks

  2. I tried to follow your tutorial but when adding let pieVC = modalVC(nibName: “modalVC”, bundle: nil) I get an error saying no such a module. I also tried to import the file in the ViewController but without any luck.

    1. Your line reads:

       let pieVC = modalVC(nibName: “modalVC”, bundle: nil) 
      

      The line in the text reads:

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

      You should be using MyModalVC
      When you created the class you a putting this in, as mentioned in the test it should read for the class declaration:

      class MyModalVC: UIViewController {
      

      and you did click the Also create XIB file when creating the file, the Xib should also be named the same.
      If everything else is correct, it look like you misspelled the name of the Xib. Swift is very case sensitive. It has to be MyModalVC if you created MyModalVC with the view controller.
      If you changed variable and class names from what I used here, as it appears in your question go back and start over, or go back and match them up correctly. Remember an important rule: capitalize all classes and types. Everything else gets lowercase.

      1. In reality, I called MyModalVC: “modalViewController”. Following your advice I renamed it ModalViewController and magically the error disappeared. Thanks for your time.

  3. import UIKit
    protocol MyModalDelegate
    {

    func myModalDidFinish(controller:PopUpViewController, pie:String)
    }
    class PopUpViewController: UIViewController
    {
    var delegate:MyModalDelegate! = nil
    var pieLabel:String? = “”

    @IBAction func pieSelectionButton(sender: AnyObject) {

    //dismissViewControllerAnimated(true, completion: nil)
    pieLabel = (sender.titleLabel?!.text)!
    print(pieLabel)
    delegate.myModalDidFinish(self, pie: pieLabel!)
    //fatal error: unexpectedly found nil while unwrapping an Optional value in last line

    }
    }

    1. did you set delegate=self in viewDidLoad? you also didn’t adpot the protocol.

  4. hello please help me i have a button and label when i click on button show a popup using xib.and we select item in popup only one that is button .title of button goback to controller and title of button set in my label

    here is my
    main viewController

    //
    // ViewController.swift
    // Dummy
    //
    // Created by User on 13/05/16.
    // Copyright © 2016 Qualwebs. All rights reserved.
    //

    import UIKit

    class ViewController: UIViewController,MyModalDelegate {

    @IBOutlet weak var statusLabel: UILabel!

    let pieVC = MyModalVC(nibName: “MyModalVC”, bundle: nil)

    @IBAction func openpop(sender: AnyObject) {
    pieVC.modalTransitionStyle = UIModalTransitionStyle.PartialCurl
    presentViewController(pieVC, animated: false, completion: nil)
    }

    func myModalDidFinish(controller: MyModalVC, pie: String) {
    print(“above function”)
    //statusLabel.text = pie
    // //print(“again in “)
    //print(statusLabel.text)
    // dismissViewControllerAnimated(true, completion: nil)

    }
    }

    and second one that is xib file

    //
    // MyModalVC.swift
    // Dummy
    //
    // Created by User on 13/05/16.
    // Copyright © 2016 Qualwebs. All rights reserved.
    //

    import UIKit
    protocol MyModalDelegate
    {
    func myModalDidFinish(controller:MyModalVC, pie:String)
    }
    class MyModalVC: UIViewController
    {
    var delegate:MyModalDelegate! = nil
    var pieLabel: String!

    @IBAction func icecream(sender: AnyObject)
    {
    pieLabel = sender.titleLabel?!.text
    delegate?.myModalDidFinish(self , pie: pieLabel)
    print(pieLabel)
    print(“by delegate view controller in function call”)
    }
    }

  5. Thank you so much for posting this tutorial.It help me lot and also saviour.Do keep Blogging.

Leave a reply to dineshramitc Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.