Make App Pie

Training for Developers and Artists

Adding Modal Views and Popovers in Swift 3.0

Modal views are one of the fundamental ways to present view controllers. Some early applications on the iPhone were completely modal views — some still do.  Modals do not do their own housekeeping like navigation controllers or tab controllers, so they are not as efficient for the backbone of your application. They need the user’s attention and suspend everything else in your UI,  making them good for settings pages and the like.

Developed originally for the iPad, Popovers are similar to modals. They are a type of view controller that requires user attention. Modals take over the window, which on the bigger real estate of the iPad, is often a waste of space. Popovers use limited space and place a smaller view on top of the main window.

Apple in iOS8 merged popovers and alerts into modals to create the current adaptive layout system. Instead of the developer having to write different code for different devices, adaptive layout does the heavy lifting and the developer writes code once. In this lesson we’ll show several ways to add popovers and modals to different devices.

Set Up the Project

First we’ll need a new project. In Xcode 6, go to File>New>Project… or hit Command-Shift-N. Create a Single-View project called Swift3PizzaPopover, set the language to Swift. Set the device to Universal, then save the project to a place you’d like.
We will be using some photos. You can use your own, here are the two I used:
pizza_sm

popover_sm

These are the full-size images. Right-Click and save the images from your browser to some folder or your desktop on your Mac. In Xcode, click on the Assets.xcassets, and from the folder you saved the images drag them into the assets work area.

2016-06-29_05-37-20

Go into the storyboard. In the lower left hand side of the storyboard, you  will find the Class Size Preview Button with the words View as: iPhone6s(wC hR)

2016-06-28_05-29-33

Click this button to show the preview bar.

2016-06-28_05-39-43

We’ll skip auto layout in this lesson and use some drag and drop layout. To make sure our controls show up on all devices we’ll lay out for the smallest device. Select the iPhone 4s device, leaving the orientation in portrait.

2016-06-29_06-02-06

 

Click the color strip in the Background Color property for the controller, In the color picker that appears, click the sliders button if not already pressed, and in the drop down change to RGB Color.

2016-06-29_06-21-50

Change the view’s background color to Hex Color #FCE8E5. Drag four buttons on the scene. On the two top buttons, remove the title and add the pizza and popover images we just loaded as the button image. On the button below the pizza image button, change the title to Pizza Modal and set the font size to 26 points. Change the title of the button to Popover and set the font size to 26 points. Arrange the buttons to something like this:

2016-06-29_06-04-54

Now drag two more view controllers out. Change each to have different background colors. I used  a background color of #D337C8  for one and the other #FDDB28

2016-06-29_06-13-36

Change to the media library by selecting the clip of film in the lower right.

2016-06-29_06-08-04

In the first view controller, add an UImageView of the pizza by dragging the pizza image to one of the new controllers. Do the same with the popover image in the other controller.

2016-06-29_06-16-54

Click the square-in-circle button to go back to the object library, and drag a label to each of these view controllers. Change the text to read BBQ Chicken Pizza and Cheddar Popover respectively. Make the font for each 20 points.

Unlike Navigation controllers, you need to explicitly dismiss a modal controller.  We’ll need a Done button. Add a button to the modal view, labeled Done at 26 points font size. Make the background Red(#FF0000) with  White(#FFFFFF) text. Popovers dismiss when anywhere outside the popover gets a touch event. However we’ll find that popovers aren’t always popovers, so cut and paste this button to the popover view.

When done, your two controllers should look like this:

2016-06-29_06-32-21

Hit Command-N to get a New File. Under iOS click Source> Cocoa Touch Class. Create a new view controller subclassing UIViewController called PizzaModalViewController. Repeat and make a UIViewController subclass called PopoverViewController Assign the subclasses to their respective view controllers using the identity inspector. Your  finished story board should look like this:

2016-06-29_06-39-06

Using Segues Directly

The easiest way of using segues and modals is to directly connect a segue from the button to the view controller. In the storyboard, control-drag from the pizza picture button  to the PizzaModalVC view. In the menu that appears, select Present Modally.

2016-06-29_06-44-57

Click on the circle for the segue and set the identifier to Pizza.

2016-06-29_07-03-41

Now for the popover, control-drag from the popover photo to the PopoverVC view. Select Present as Popover.

2016-06-29_06-48-03

Select the segue, then set the identifier to Popover in the attributes inspector.

2016-06-29_06-50-03

Select an iPad Air 2 in the simulator. Build and run.

2016-06-29_06-53-27

Click on the Popover picture. You will get this:

2016-06-29_06-54-37

Tap anywhere but the popover and the popover disappears. Tap the pizza picture, and the pizza appears from the bottom. However, we can’t get rid of it.

2016-06-29_06-56-03

We did not yet make the dismissal code. Stop the simulator, and go back to the storyboard. Bring up the assistant editor, and select the PizzaModalVC view. Connect the Done button to the view controller code by selecting the Done button and then control dragging the button to the view controller.  Select an action for the connection as a UIButton. Call this pizzaModalDone Do the same for the popover, calling the method popoverDone.

In the PizzaModalVC class add the following to pizzaModalDone:

@IBAction func pizzaModalDone(_ sender: UIButton) {
    dismiss(animated: true, completion: nil)
}

In the PopoverVC class, add the following to popoverDone:

@IBAction func popoverDone(_ sender: UIButton) {
    dismiss(animated: true, completion: nil)
}

We dismiss any modal view controller with dismiss.  It is a method of the modal view controller, hence we are calling it from within the class. It has two arguments. The first indicates if an animation should   accompany dismissal. This is the usual way to dismiss, However for speed when dismissing a modal view behind another view, you can set this to false. The second argument is a closure if you need to do anything after successfully dismissing the view controller. Generally I leave it nil.

Build and run. Now both Done buttons work, and well as clicking in the shaded area outside the popover.

Presenting a Segue Programmatically

Go back to the storyboard. In our main view controller add this above viewDidLoad():

@IBAction func openPizzaModal(_ sender: UIButton) {
        performSegue(withIdentifier:"Pizza", sender: self)
    }
    @IBAction func openPopover(_ sender:UIButton){
        performSegue(withIdentifier:"Popover", sender: self)
    }

Connect the openPizzaModal(sender:) to the Pizza Modal button and the openPoppover(sender:) to the Popover button by dragging from the circle to the correct button. Build and run. Now you can use the bottom text buttons as well as the photos. Line 2 and 5 use the performSegue(withIdentifier:) method. When we set up our segues we gave them identifiers. We’ve used that identifer to call the segue. If you want all your buttons to use segues programmatically, control drag from the source view controller icon in the storyboard to the destination view controller.

Programmatically Presenting a Modal View Controller from the Storyboard

When using a storyboard, we can use the storyboard identifier to do that.  Go to the story board and select the pizza view controller. In the Identity inspector change the Storyboard ID to Pizza

2016-06-29_08-13-46

We’ll use this identifier to call our view controller from the storyboard. Add another button Pizza Prog with 26 point type to the root controller next to the Pizza Modal Button

2016-06-29_08-18-00

Open the assistant editor and control drag the new button tot he code. Make another action openPizzaProgModal in the ViewController class.

In ViewController.swift and change openPizzaProgModal to this:

@IBAction func openPizzaProgModal(_ sender: UIButton) {
    let vc = (
        storyboard?.instantiateViewController(
        withIdentifier: "Pizza")
    )!
    vc.view.backgroundColor = UIColor.orange()
    vc.modalTransitionStyle = .crossDissolve
    present(vc, animated: true, completion: nil)
}

Line two gets the view controller from the storyboard using the Storyboard Identifier we set up. If these two do not not match exactly you will get a fatal error.

Line 8 is the core of this. The present method presents the modal view, with animation and no completion handler.  It’s sort of the bracket to the dismiss method, with almost the same arguments. There is one difference. In dismiss, the object dismissed calls dismiss. In present, the current view controller calls present, and the first argument is the view controller to be presented.  Pretty much all presentation of view controllers look exactly like this.

When using present, there is no prepare(for segue:)  equivalent, which you would have with performSegue or running from a storyboard segue.  Once we have the view controller,  we change its properties directly, such as changing the background to orange in line 6. We can also change the transition to a cross dissolve, using the property modaltransitionStyle.

Build and run. Select the Pizza Modal button and you will see our flip transition and a different layout than the segue version.

2016-06-29_12-00-21

Programmatically Presenting a Modal View

The present method can be used with any view controller. If you made your own programmatic one, you need only an instance of the view controller to present it.

Press Command-N and add a new Cocoa Touch Class named PizzaModalProgViewController subclassing UIViewController.

I won’t go into details about this code, but suffice it to say it creates a view controller with a blue Done button, a label, and an image.

class PizzaModalProgViewController: UIViewController {
    let dismissButton:UIButton! = UIButton(type:.custom)
    var imageName = "pizza_sm"
    var text = "BBQ Chicken Pizza!!!!"
    
    let myLabel = UILabel()
    
    func pizzaDidFinish(){
        dismiss(animated: true, completion: nil)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Build a programmatic view
        
        //Set the Background color of the view.
        view.backgroundColor = UIColor(
            red: 0.8,
            green: 0.6,
            blue: 0.3,
            alpha: 1.0)
        // Add the image to the view
            let myImage:UIImage! = UIImage(named: imageName)
            let myImageView = UIImageView(image: myImage)
            myImageView.frame = view.frame
            myImageView.frame = CGRect(
                x: 10,
                y: 10,
                width: 200,
                height: 200)
            view.addSubview(myImageView)
        // Add the label to the view
        myLabel.text = text
        myLabel.frame = CGRect(
            x: 220,
            y: 10,
            width: 300,
            height: 150)
        myLabel.font = UIFont(
            name: "Helvetica",
            size: 24)
        myLabel.textAlignment = .left
        view.addSubview(myLabel)
        
    //Add the Done button to the view.
        let normal = UIControlState(rawValue: 0) //.normal doesn't exist in beta. workaround
        dismissButton.setTitle("Done", for: normal)
        dismissButton.setTitleColor(UIColor.white, for: normal)
        dismissButton.backgroundColor = UIColor.blue
        dismissButton.titleLabel!.font = UIFont(
            name: "Helvetica",
            size: 24)
        dismissButton.titleLabel?.textAlignment = .left
        dismissButton.frame = CGRect(
            x:10,
            y:275,
            width:400,
            height:50)
        dismissButton.addTarget(self,action: #selector(self.pizzaDidFinish),for: .touchUpInside)
        view.addSubview(dismissButton)
    }
}

Go back to ViewController.swift and the openPizzaModal action. Comment out the code for the storyboard and add a few new lines of code above it.

@IBAction func openPizzaProgModal(_ sender: UIButton) {
   //using a view controller
    let vc = PizzaModalProgViewController()
    vc.modalTransitionStyle = .partialCurl
    present(vc, animated: true, completion: nil)
 
/* //using a story board
    let vc = (storyboard?.instantiateViewController(
        withIdentifier: "Pizza"))!
    vc.view.backgroundColor = UIColor.orange
    vc.modalTransitionStyle = .crossDissolve
    present(vc, animated: true, completion: nil)
 */       
}

Since the present method uses a view controller, line 3 gets an instance of one. Line 4 changes the transition style again, this time to a page curl transition. Line 5 presents the controller, looking identical to line 12 when we presented a view controller from the storyboard..

Build and run. Click the Pizza Prog button to get a different modal than before.

2016-06-30_05-38-17

Programmatically Presenting a Popover

How to present a popover is similar to calling a modal programmatically. There are a few differences however. Change the view controller method openPopover as follows, commenting out the previous code:

@IBAction func openPopover(_ sender:UIButton){
    //performSegue(withIdentifier:"Popover", sender: self)
    let vc = PizzaModalProgViewController()
    //change properties of the view controller
    vc.text = "Cheesy!!"
    vc.imageName = "popover_sm"
    vc.view.backgroundColor = UIColor.yellow
    //present from a view and rect
    vc.modalPresentationStyle = .popover //presentation style
    present(vc, animated: true, completion: nil)
    vc.popoverPresentationController?.sourceView = view
    vc.popoverPresentationController?.sourceRect = sender.frame
}

Line three will give us the same view controller as the last example. As discussed  earlier, lines 5 through 7 set properties of the view controller, this time changing properties to use a different image and text than the default values of the class. The code sets a yellow background as well.

Line 9 tells the system to present this modal as a popover. There are two ways to present a popover, one for popovers originating in toolbar buttons and one which presents a popover attached to a CGRect and displayed in some view. Both start the same. We set a modal presentation style of .popover, then present the popover like the modal view we did for the pizza.

The difference between popovers  on a CGRect and on a bar button is in the next two lines, which must come immediately after the presentation. These two lines of code set up the popover that anchors to a CGRect and presents itself in the space of a UIView. When we use a popover to present, we get a popoverPresentationController on the modal view. This controls the appearance and presentation of the popover. The property sourceView of popoverPresentationController indicates the view that the popover will appear, which often is view. The property sourceRect indicates the CGRect that the popover will be anchored to. In this example I attached the popover to the button that opens it, which is a common occurrence.

Now build and run. Select the popover button, and it works.

2016-06-30_06-05-06

Popovers often appear from bar button items in a toolbar or navigation bar. While we need two properties for a popover off of a CGRect, for a bar button item you need only one, appropriately named barButtonItem. Though we do not use it on our project, an equivalent code for using a bar button item might be this:

@IBAction func openPopover(sender:UIBarButtonItem){
    let vc = PopoverProgVC()
    vc.modalPresentationStyle = .Popover
    presentViewController(vc, animated: true, completion: nil)
    vc.popoverPresentationController?.barButtonItem = sender      
}

You can see an example of bar button items  in the lesson about UIImagePickerController. They look like this:

2016-06-28_08-14-44

Size Classes, Modals, and Popovers

Prior to iOS8, popovers and modals were different. Starting with iOS8, popovers became a type of modal. This was due to size classes. While I go into the details about size classes in my books Swift Swift View Controllers and Practical Autolayout, the basics of size classes is that your layout changes and adapts to different size screens and orientations automatically.

Before iOS8, if you ran a popover on any iPhone, you would get an error. Now iOS just adapts. For most phones in most orientations you get a .fullscreen modal presentation. Try it. Select an iPhone 6s in the simulator, and run the app again. Select a popover and you get a modal in portrait and landscape:

2016-06-30_06-11-402016-06-30_06-14-39

The iPhone 6 plus and 6s plus in landscape will give you a .formsheet presentation style for a popover, but  .fullscreen in Portrait. Try iPhone 6s plus in the simulator.

2016-06-30_06-17-28 2016-06-30_06-17-51

Starting with iOS9 on iPads, we also have mutitasking, which will act like an iPhone for popovers. Run the simulator With an iPad air 2. I rotated my screen for better effect by pressing Command-right arrow:

2016-06-30_06-21-06

With the simulator running, hit Command-Shift-H to get back to the home screen. Select Safari as an app. Drag from the right edge of the screen towards the center.

2016-06-30_06-23-59

In the icons that appear, pick the Swift3PizzaModal app, then once loaded, click the modal once again.

2016-06-30_06-31-36

On the left you will have Safari, on the right our App, though compressed. Try the popover again and you get a .fullscreen popover.

2016-06-30_06-24-22

Drag the white bar to the left of our app, and the bar turns black. Move it to the center. We still get a .fullScreen.

2016-06-30_06-24-49

Instead of having to figure out any of these cases, the adaptive layout features of iOS does it for you. For popovers, views with a compact width get a .fullsheet modal view and ones with a regular width get a popover or .formsheet.

We’ve covered the basics of popovers and modals. Modals work anywhere, on iPhone or iPad. Popovers and some special case modals work or appear differently on iPad only. In this lesson we saw how similar popovers are to modals. There is a few more things to do with modals which we will still need to get to. In future posts, we’ll discuss the one popover bug that will definitely get you kicked out of the app store, and how to avoid it.

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  Swift3PizzaPopover
//
//  Created by Steven Lipton on 6/29/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//
// Demo of various ways or presenting modal view controllers

import UIKit

class ViewController: UIViewController {

//
//Programmatic ways of presenting modals
//the goal is to get a view controller and 
// present it with present(vc, animated: true, completion: nil)
//
// for a storyboard identifier, we use  
//    let vc =  (storyboard?.instantiateViewController(
            withIdentifier: "Pizza"))!
//for a view controller with programmatic layout we use 
//    let vc = init()
//

    @IBAction func openPizzaProgModal(_ sender: UIButton) {
       /* //using a story board
        let vc = (storyboard?.instantiateViewController(
            withIdentifier: "Pizza"))!
        vc.view.backgroundColor = UIColor.orange
        vc.modalTransitionStyle = .crossDissolve
        present(vc, animated: true, completion: nil)
       */
        
        //using a view controller
        let vc = PizzaModalProgViewController()
        vc.modalTransitionStyle = .partialCurl
        present(vc, animated: true, completion: nil)
    }
//
// To present a modal from its segue identifier use  this
//    performSegue(withIdentifier:String, sender: self)

    @IBAction func openPizzaModal(_ sender: UIButton) {
        performSegue(withIdentifier: "Pizza", sender: self)
    }
//
// Presenting popovers
// Popovers are a special presentation style of a modal view controller
// which present a small window on iPads, and a  full screen modal on other devices
// From a segue, you present it the same way as any other modal 
// From a view controller, you need two more components
//
// 1) set the modalPresentationStyle property to .popover 
//    vc.modalPresentationStyle = .popover
// 2) Tell the popover where to present. This code goes immediately after the 
// present() call. If you are anchoring to a view, you need two lines of code    
//        vc.popoverPresentationController?.sourceView = viewToPresentIn
//        vc.popoverPresentationController?.sourceRect = aCGRectToAnchorTo
//
//If anchoring to a UIBarButtonItem, you need only one line of code
//   vc.popoverPresentationController?.barButtonItem = aBarButtonItem
//
    @IBAction func openPopover(_ sender:UIButton){
    /* //Present a popover from a segue
        performSegue(withIdentifier:"Popover", sender: self)
    */
    //Present a popover programmatically
        let vc = PizzaModalProgViewController()
        vc.modalPresentationStyle = .popover
        //change properties of the view controller
        vc.text = "Cheesy!!"
        vc.imageName = "popover_sm"
        vc.view.backgroundColor = UIColor.yellow
        //present from a view and rect
        present(vc, animated: true, completion: nil)
        vc.popoverPresentationController?.sourceView = view
        vc.popoverPresentationController?.sourceRect = sender.frame
        /*
         //Code to present popover from a bar button Item,
          //though we don't have one in this example.
         present(vc, animated: true, completion: nil)
         vc.popoverPresentationController?.barButtonItem = sender
        */
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
}

PizzaModalViewController.swift

//
//  PizzaModalViewController.swift
//  Swift3PizzaPopover
//
//  Created by Steven Lipton on 6/29/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class PizzaModalViewController: UIViewController {


//
// Dismissal code for any modal 
//
    @IBAction func pizzaModalDone(_ sender: UIButton) {
        dismiss(animated: true, completion: nil)
    }

}

PopoverViewController.swift

//
//  PopoverViewController.swift
//  Swift3PizzaPopover
//
//  Created by Steven Lipton on 6/29/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class PopoverViewController: UIViewController {

//
// Dismissal code for any modal 
//
// Note on popovers this is not necessary, since any touch outside the popover
// dismisses the popover. However, if presented as a full sheet on a compact width device, 
// this is necessary. For code to hide the done button based on traits, 
// see  my book Swift Swift View Controllers

    @IBAction func popoverDone(_ sender: UIButton) {
        dismiss(animated: true, completion: nil)
    }
}

PizzaModalProgViewController.swift

//
//  PizzaModalProgViewController.swift
//  Swift3PizzaPopover
//
//  Created by Steven Lipton on 6/29/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//
//  Code to programmatically create a View Controller. 
//  Explanation of this code is beyond the scope of this lesson
//
//
import UIKit

class PizzaModalProgViewController: UIViewController {
    let dismissButton:UIButton! = UIButton(type:.custom)
    var imageName = "pizza_sm"
    var text = "BBQ Chicken Pizza!!!!"
    
    let myLabel = UILabel()
    
    func pizzaDidFinish(){
        dismiss(animated: true, completion: nil)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Build a programmatic view
        
        //Set the Background color of the view.
        view.backgroundColor = UIColor(
            red: 0.8,
            green: 0.6,
            blue: 0.3,
            alpha: 1.0)
        // Add the image to the view
            let myImage:UIImage! = UIImage(named: imageName)
            let myImageView = UIImageView(image: myImage)
            myImageView.frame = view.frame
            myImageView.frame = CGRect(
                x: 10,
                y: 10,
                width: 200,
                height: 200)
            view.addSubview(myImageView)
        // Add the label to the view
        myLabel.text = text
        myLabel.frame = CGRect(
            x: 220,
            y: 10,
            width: 300,
            height: 150)
        myLabel.font = UIFont(
            name: "Helvetica",
            size: 24)
        myLabel.textAlignment = .left
        view.addSubview(myLabel)
        
    //Add the Done button to the view.
        let normal = UIControlState(rawValue: 0) //UIControlState.normal doesn't exist in beta(#27105189). This is a workaround.
        dismissButton.setTitle("Done", for: normal)
        dismissButton.setTitleColor(UIColor.white, for: normal)
        dismissButton.backgroundColor = UIColor.blue
        dismissButton.titleLabel!.font = UIFont(
            name: "Helvetica",
            size: 24)
        dismissButton.titleLabel?.textAlignment = .left
        dismissButton.frame = CGRect(
            x:10,
            y:275,
            width:400,
            height:50)
        dismissButton.addTarget(self,action: #selector(self.pizzaDidFinish),for: .touchUpInside)
        view.addSubview(dismissButton)
    }

}

18 responses to “Adding Modal Views and Popovers in Swift 3.0”

  1. Hi, Thank you for the tutorial. I get this error when trying to present Programmatically Presenting a Popover. This line lights up:
    –* present(vc, animated: true, completion: nil) *– with error:
    “Use of instance member ‘instantiateViewController’ on type ‘UIStoryboard’; did you mean to use a value of type ‘UIStoryboard’ instead?”

    Any help appreciated pls

    1. That line shows up multiple times in the code. Which one were you referring to?

      1. var cell : SupportTableViewCell!
        var supports = [Cell]()

        override func viewDidLoad() {
        super.viewDidLoad()

        // Load the sample data.
        loadSampleModules()
        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem()
        }

        func loadSampleModules() {
        let photo1 = UIImage(named: “Logo”)!
        let support1 = Cell(name: “Counsellors or Psychologists”, photo: photo1, descriptionLabel: “-“)!

        let photo2 = UIImage(named: “Logo”)!
        let support2 = Cell(name: “Law enforcement”, photo: photo2, descriptionLabel: “-“)!

        let photo3 = UIImage(named: “Logo”)!
        let support3 = Cell(name: “Financial”, photo: photo3, descriptionLabel: “-“)!

        let photo4 = UIImage(named: “Logo”)!
        let support4 = Cell(name: “Legal”, photo: photo4, descriptionLabel: “-“)!

        let photo5 = UIImage(named: “Logo”)!
        let support5 = Cell(name: “Fire Department”, photo: photo5, descriptionLabel: “-“)!

        let photo6 = UIImage(named: “Logo”)!
        let support6 = Cell(name: “Medical”, photo: photo6, descriptionLabel: “-“)!

        supports += [support1, support2, support3, support4, support5, support6]
        tableView.reloadData()
        }

        @IBAction func backTapped(_ sender: UIBarButtonItem) {
        /*
        Depending on style of presentation, this view controller needs to be
        dismissed in two different ways. We used a modal presentation for
        adding people, which needs to be dismissed. Editing an existing person
        used a ‘push’, which requires a ‘pop’ to return to the previous page.
        */
        self.dismiss(animated: true, completion: nil)

        }

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

        // MARK: – Table view data source

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

        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return supports.count
        }

        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = “SupportTableViewCell”
        cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! SupportTableViewCell

        // Fetches the appropriate meal for the data source layout.
        let support = supports[indexPath.row]

        cell.supportName.text = support.name
        cell.supportImage.image = support.photo

        // Configure the cell…
        return cell
        }

        @IBAction func openPopover(_ sender:UIButton){
        //performSegue(withIdentifier:”Popover”, sender: self)
        let vc = PopoverViewController()
        //change properties of the view controller
        vc.text = “Cheesy!!”
        vc.imageName = “popover_sm”
        vc.view.backgroundColor = UIColor.yellow
        //present from a view and rect
        vc.modalPresentationStyle = .popover //presentation style
        /* This line */ present(vc, animated: true, completion: nil) // <==
        vc.popoverPresentationController?.sourceView = view
        vc.popoverPresentationController?.sourceRect = sender.frame
        }
        }

      2. That’s your code, not the demo. I didn’t cover table view cells. I don’t fix others people’s code.

  2. Hi Josh. Very nice tutorial – thanks for sharing! I reproduced your tutorial as described above and it works fine except for one thing. For some reason the popover insists on behaving like a fullscreen mode either by using the simple segue or by presenting it programmatically. I am using Swift 3 and Xcode 8.1. Any thoughts what is going on?

    1. What simulator are you using? if an iphone, try an ipad.

      1. I tried a few different iPads and iPhones. I do get a debug message: ”
        Popovers[5879:284106] – changing property magnificationFilter in transform-only layer, will have no effect” —- I checked this out and the only things I found relating to this suggested its an Xcode bug.

  3. I should add that I have restarted Xcode and my computer too.

    1. oh if you are using josh’s code, I can’t help you.

      1. I did you your (Steven Lipton) code…. to the letter. Sorry I replied in the wrong place!

      2. ill have to look into it then.

      3. I just ran the code I used in the example in Xcode 8.2 beta without a hitch. There may be something wrong with the simulator. Try adding this code as the first line of openPopover :

        print(self.traitCollection.horizontalSizeClass.rawValue)

        On an iPad simulator you should get 2 (regular width)printed on the console when you tap the popover button. If you get 1(compact width), then there’s something wrong with the simulator or something is setting your iPad simulator to compact width. If you see no number in the console, I;d suggest checking if you wired the actions right, and modalpresentationstyle is popover.

  4. You called it Steven. I saw a 1 when I did this. I changed my device orientation to universal (saw a 2) and it worked just fine. Dumb mistake on my part. I actually was looking for a popUp for an iPhone. You inspired me to look further and I eventually ended up using a UIPresentationController subclass. Again… great tutorial and great follow up. Thanks, I appreciate your help.

    1. Those mistakes are the ones we learn the most from. Glad it all worked out and got you thinking in there directions s well.

  5. Wonderful tutorial — probably the clearest one I’ve seen since getting started learning Swift. Very nicely done!

    One question: when closing the popover, the following error comes up:

    My_UI_Play1[95218:7914784] [Warning] is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.

    Searching for that, it seems like the usual conclusion is “yeah, it’s a bug, but don’t worry about it.” Is that accurate? Is there anything we can do to avoid getting this message? (Xcode 8.1, Swift 3, iOS 10.1)

    Thanks!

  6. I have two VC (MainVC & AddVC)

    AddVC is a popover and has a save button called “Save”
    When I tap save it dismisses the popover like it it suppose to BUT the MainVC is a TableView and it doesn’t load new data.

    How do you correctly tell that MainVC to reload data when save is pressed?

    1. I’m not sure how you are changing the data source for the table. How you would do that changes what you can and cannot do. Your two best bets would be using on your table view property in your MainVC either func reloadData()
      for the brute force method. or if you have the indexPaths available, func reloadSections(IndexSet, with: UITableView.RowAnimation)

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 )

Facebook photo

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

Connecting to %s

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

%d bloggers like this: