How to Use UIImagePickerController for a Camera and Photo Library in Swift 3.0.

2016-06-28_08-20-54

Almost every iPhone and iPad now has a camera. Many people want to use a camera in their apps for various reasons. While you may not be building the next Instagram or Twitter, there are many places where photo documentation comes in handy.

There are two ways to use the camera. The more powerful and advanced method is using AVFoundation, but it is also a very complicated way to get a photo out of the camera.

The simpler way is the UIImagePicker. This is a quick way as Swift has simplified much of the code for it.  It is also the best way to fetch photos from the photo album. In this lesson, we’ll set up a basic camera and library app. In upcoming posts we’ll add a few embellishments to it that will make for a more effective camera.

Set Up The Layout

We’ll be talking about some of the issues with camera on both iPad and iPhone, so we’ll make this a universal app.  We will be using a dab of auto layout as well. If you want a brief introduction to auto layout go over and take a look at my book Practial Autolayout. You won’t need it for this lesson, but it may make what I do a little more understandable.

Make a new project SwiftPizzaCam with a single view and using Swift. As already mentioned, make the device Universal.

If you are not familiar with Xcode 8’s interface builder bar and the auto layout icons, here is a guide to help you as we go through setting up the app.

2016-06-28_05-29-33

Click on the iPhone 6s class preview. A new selection of previews appears.

2016-06-28_05-39-43

Select the iPad 9.7″ device.  then zoom out to 50%

2016-06-28_05-55-24

Xcode 8 come with a series of easily accessible preview modes in interface builder.  We will put a photo into an UIImageView when we first  use the image, and doing so on an iPad is easier than an iPhone, though it will work on both.  Right-click and save the image below:

pizza

Click on Assets.Xcassets and drag the pizza file into the assets folder. Go back to the story board and select the clip icon.  You will find the pizza media there.

2016-06-28_05-49-14

Drag out the pizza to the view and drop it into the view controller.

2016-06-28_05-58-42

Click the pin pinMenuButtonbutton.  Click off Constrain to Margins. Click on all the I-beams, then set their value to 0 points  like the image below:

2016-06-28_06-01-52

Be sure to press tab after you type in any number in this popover. It has an annoying habit of forgetting them if you don’t. Select Update Frames: Items of New Constraints towards the bottom.  Click  Add 4 Constraints. In the properties change the View Mode to AspectFit to properly size the photo.

Screenshot 2014-12-03 08.48.32

Now drag out a label to the storyboard. Set the background property to a Light Gray( #AAAAAA) color with a 65% alphaCenter Align the label text with a 28 point font size. Change the text of the label to read Pizza Cam!!!.  Select the label,  and then  click the pin button pinMenuButton. Set the top  8 points,  left and right sides 0 points, but not the bottom, like this:

2016-06-28_06-24-32

Make  sure you select Update Frames: Items of New Constraints towards the bottom before clicking  Add 3 Constraints.

Drag out  a toolbar and place toward the bottom of the view. Using the pin menu pinMenuButton, Pin it to the bottom, left and right like this, again updating the frames:

2016-06-28_06-21-16

Add a bar button item and a flexible space bar item to the toolbar. In one of the two bar buttons label it Photo and the other Library. Your tool bar and layout should look like this:

2016-06-28_06-26-34

 

Select the iPhone 6s in the preview modes. It should look like this:

2016-06-28_06-27-22

If you did everything correctly, you should have no auto layout warnings. If you do, go to the resolver and click Update Frames on the bottom half of the menu that appears.  If things get messier after doing this, clear the constraints on the bottom of the resolver, and pin everything again.

Wire Up the Outlets

Your next step is to wire up all the outlets and actions. Open the assistant editor.  Control-drag from the pizza photo  image and make an outlet called myImageView. Control drag from the Library button and make an action called photoFromLibrary with a sender of type UIBarButtonItem. This is important for stuff we will do later. Do this again, but with the Photo button and named shootPhoto. Again, make the sender UIBarButtonItem.
You can clean up the code a bit if you like. When done you should have something like this:

import UIKit

class ViewController: UIViewController{
    @IBOutlet weak var myImageView: UIImageView!

    @IBAction func shootPhoto(_ sender: UIBarButtonItem){
}
    @IBAction func photofromLibrary(_ sender: UIBarButtonItem) {
   }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    }
}

Add the UIImagePickerController and Delegate

The star of our show the UIImagePickerController is missing. We do that programmatically. Close the assistant editor, and open ViewController.swift. Add the following line under the outlet for myImageView:

let picker = UIImagePickerController()

The UIImagePicker does much of its works from a delegate. Add the following to the class description:

class ViewController: UIViewController, 
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate {

We actually have two delegates: UIImagePickerControllerDelegate and UINavigationControllerDelegate. The UINavigationControllerDelegate is required but we do nothing with it. In the viewDidLoad, add the following:

 override func viewDidLoad() {
        super.viewDidLoad()
        picker.delegate = self
    }

We have wired up the delegate. Unlike other delegates, there is no required methods. However, this will not work without implementing two methods. At the bottom of the class, add the following:

//MARK: - Delegates
func imagePickerController(_ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [String : AnyObject])
{
        
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        
}

These two methods handle our selections in the library and camera. We can either handle the cancel case with imagePickerControllerDidCancel or handle media with didFinishPickingMediaWithInfo

Getting a Photo from the Library

The UIImagePickerController is a view controller that gets presented modally. When we select or cancel the picker, it runs the delegate, where we handle the case and dismiss the modal. Let’s implement the photo library first, then the delegates. Add the following code to the photoFromLibrary method:

@IBAction func photoFromLibrary(_ sender: UIBarButtonItem) {
   picker.allowsEditing = false
   picker.sourceType = .photoLibrary
   picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
   present(picker, animated: true, completion: nil)
}

To get to the photo picker, it is three lines of code. We already initialized picker. In line two, we tell the picker we want a whole picture, not an edited version. In line three, we set the source type to the photo library. Line four we set the media types for all types in the photo library. Line five calls present to present the picker in a default full screen modal.

If you build and run, you could press the photoFromLibrary method, but then get stuck in the library. We need delegates to get out of the library. First let’s add the code for the imagePickerDidCancel delegate method:

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
       dismiss(animated: true, completion: nil)
    }

This does nothing special: it dismisses the modal controller. If we do pick a photo, we want to do something with the photo. Add the following code to the didFinishPickingMediaWithInfo delegate method:

func imagePickerController(_ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [String : AnyObject])
{
    let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage //2
    myImageView.contentMode = .scaleAspectFit //3
    myImageView.image = chosenImage //4
    dismiss(animated:true, completion: nil) //5
}

One of the parameters of this method is info. It has a dictionary of various information about the selected media, including metadata,  a user edited image if the .allowsEditing property is true, and a NSURL location of the image. For our camera app, the important key is the UIImagePickerControllerOriginalImage. We take that key and put the image into a variable. We could make this a constant, but there is some future iterations of this project that will need this as a variable.

Camera images are usually bigger than a UIImageView on a device. To shrink them quickly to a visible size, the best way is to set the .contentView property of our UIImageView to .ScaleAspectFit as we do in line 3.

Security for the Photo Library

Run in the simulator as an iPhone 6s. You should get a screen like this:

2016-06-28_07-07-54

Tap the Library bar button. The app crashes:

2016-06-28_07-08-30

In the simulator, there’s no message about what happened, the system just ends the application. Apple requires any use of the camera, the photo library or any personal information for that matter asks the user to agree sharing information with the app on the first use. For the UIImagePicker, this is included in your first run of the app.  Starting with iOS10, there’s another layer of security on the photo library. The developer must make an entry in the info.plist describing why they want to use the photo. That description will show up in an alert when the user decides to allow access to the library.

There’s two ways to add this entry: in XML or in the property list. We’ll use the property list first.  In Xcode,  select Info.plist from the Navigator pane

2016-06-28_07-21-27

Right click on the Information Property List Row. In the menu that appears, select Add Row.

2016-06-28_07-22-27

You get a new row with a drop down menu. Select Privacy – Photo Library Usage… from the menu.

2016-06-28_07-28-07

With the row still highlighted, click the value column  and add why you want access to the photo library.  In this app we are setting a background image.

2016-06-28_07-31-16

If you wish to add this directly to XML, the key/value is this

<key>NSPhotoLibraryUsageDescription</key>
<string>Set the Background</string>

We’ll come back to this for the camera, and do it in XML.

Build and Run again. Click the Library button, and you will get the permissions alert with the description we placed in the property list:

2016-06-28_07-33-57

Tap OK.  We get our Image picker,

2016-06-28_07-41-08     2016-06-28_07-41-29

When we tap a photo, it appears on the app.

2016-06-28_08-00-41

Running as an iPhone and iPad app: Using Popovers

Stop the app and run as an iPad 2. After answering OK to the library privacy question you get this:

2016-06-28_08-04-30

Which looks fine, except Apple doesn’t like it. They want you to use a  popover. Popovers must access the photo library on iPads.   Fortunately, it is very easy to get the popover to work. Add the highlighted lines in photoFromLibrary

    @IBAction func photoFromLibrary(_ sender: UIBarButtonItem) {
        picker.allowsEditing = false
        picker.sourceType = .photoLibrary
        picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
        picker.modalPresentationStyle = .popover
        present(picker, animated: true, completion: nil)
        picker.popoverPresentationController?.barButtonItem = sender
    }  

Line 5 selects a presentation style of a popover. We then present the popover. Line 7 sets the reference point for the popover to the bar button item. As I mentioned in another post popovers need a reference rectangle to pop up from. In this case, we can use the UIBarButtonItem which happens to be sender . This is why it was so important to use UIBarButtonItem as the sender type.

Build and run with the iPad 2 simulator. Now we have a popover to pick a photo.

2016-06-28_08-14-44

 

Since present checks the class size, you will get the proper behavior on any phone .The iPhone 6s Plus in the simulator does this.

2016-06-28_08-20-16 2016-06-28_08-20-54

 

We save ourselves from hours of device fragmentation work this way.

Adding a Camera

Basic Camera code is almost the same as adding a photo library. Change the shootPhoto code to this:

@IBAction func shootPhoto(_ sender: UIBarButtonItem) {
    picker.allowsEditing = false
    picker.sourceType = UIImagePickerControllerSourceType.camera
    picker.cameraCaptureMode = .photo
    picker.modalPresentationStyle = .fullScreen
    present(picker,animated: true,completion: nil)
}

We changed the sourceType property to .camera and specified the cameraCaptureMode property to .photo. The camera, unlike the photo library, is required to be full screen, so we don’t make it a popover, but explicitly a .FullScreen.

Like the library, we have another info.plist entry to make. This time we’ll use XML. Right click the info.plist and in the menus select Open As> Source Code:

2016-06-28_08-48-01

Just below the tag <dict>add this:

<key>NSCameraUsageDescription</key>
<string>Set the background of the app with your beautiful photography</string>

We’re ready to run. However, if you build and run this in the simulator, you will crash. In order to test and use camera code you have to use a real connected device. I connected my iPad Pro and ran the code. When I pressed the photo button, I again get the message about allowing access.

IMG_0379

Tapping OK, I get the camera:

IMG_0381

Take a picture, and the app asks me if I want to keep it.

IMG_0382

The app returns with my photo.

IMG_0383

Preventing the Camera Crash

This all works, but we really want to prevent that crash if there is no camera. Add the highlighted lines to shootPhoto:

 @IBAction func shootPhoto(_ sender: UIBarButtonItem) {
    if UIImagePickerController.isSourceTypeAvailable(.camera) {
        picker.allowsEditing = false
        picker.sourceType = UIImagePickerControllerSourceType.camera
        picker.cameraCaptureMode = .photo
        picker.modalPresentationStyle = .fullScreen
        present(picker,animated: true,completion: nil)
    } else {
        noCamera()
    }
}

Line 2 uses the class method isSourceTypeAvailable to checks if we have a camera. If there is, run the camera. If not explain to the user the device does has noCamera. For that function, we’ll add an alert like this, using UIAlertController:

func noCamera(){
        let alertVC = UIAlertController(
            title: "No Camera",
            message: "Sorry, this device has no camera",
            preferredStyle: .alert)
        let okAction = UIAlertAction(
            title: "OK",
            style:.default,
            handler: nil)
        alertVC.addAction(okAction)
        present(
            alertVC,
            animated: true,
            completion: nil)
    }

Line 2 makes an alert with the proper message. We add an OK action button on lines 6 through 10, then present the alert as a modal in line 11 onwards. With this code, if you run in the simulator, you get this when you attempt to take a picture:

2016-06-28_09-29-39

The Basics, but Wait! There’s more!

Basic UIImagePickerController is relatively easy to implement, but there are a lot of issues it leaves hanging:

  • Hitting no for privacy settings for  accessing the camera or library
  • Dealing with more than one popover
  • Customizing and adding more controls for the camera
  • Adding and playing video
  • Using  and storing pictures
  • Using a UIScrollView to zoom and scroll around a picture.
  • Getting rid of  the memory warnings
  • Wanting more power over my camera controls
  • Sharing pictures with my friends

Many of these could be questions that can be answered in context with the camera app, or outside of that context. In other lessons I’ll be answering them both ways.

The Whole Code

//
//  ViewController.swift
//  SwiftPizzaCam
//
//  Created by Steven Lipton on 6/28/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import UIKit

class ViewController: UIViewController,
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate
{
    let picker = UIImagePickerController()
    @IBOutlet weak var myImageView: UIImageView!
    
    @IBAction func photoFromLibrary(_ sender: UIBarButtonItem) {
        picker.allowsEditing = false
        picker.sourceType = .photoLibrary
        picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
        picker.modalPresentationStyle = .popover
        present(picker, animated: true, completion: nil)
        picker.popoverPresentationController?.barButtonItem = sender
    }
    
    @IBAction func shootPhoto(_ sender: UIBarButtonItem) {
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            picker.allowsEditing = false
            picker.sourceType = UIImagePickerControllerSourceType.camera
            picker.cameraCaptureMode = .photo
            picker.modalPresentationStyle = .fullScreen
            present(picker,animated: true,completion: nil)
        } else {
            noCamera()
        }
    }
    func noCamera(){
        let alertVC = UIAlertController(
            title: "No Camera",
            message: "Sorry, this device has no camera",
            preferredStyle: .alert)
        let okAction = UIAlertAction(
            title: "OK",
            style:.default,
            handler: nil)
        alertVC.addAction(okAction)
        present(
            alertVC,
            animated: true,
            completion: nil)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        picker.delegate = self
    }

    //MARK: - Delegates
    func imagePickerController(_ picker: UIImagePickerController,
        didFinishPickingMediaWithInfo info: [String : AnyObject])
    {
        var  chosenImage = UIImage()
        chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage //2
        myImageView.contentMode = .scaleAspectFit //3
        myImageView.image = chosenImage //4
        dismiss(animated:true, completion: nil) //5
    }
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
       dismiss(animated: true, completion: nil)
    }

}

23 Replies to “How to Use UIImagePickerController for a Camera and Photo Library in Swift 3.0.”

  1. Hi, is it possible to create a simple app that allows selection of multiple photos instead of just one? I know uiimagepickercontroller only allows one photo at a time. There is other custom picker controller like ELC but it is written in obj-C and it requires bridging. Thank you!

    1. I have exactly the same issue. I have 4 image views and 4 gesture controllers and i want each view to display and retain the photo that was selected to it. I am pulling my hair out now. Steve help me pls!!!

  2. how to use photoLibrary for iPad in Landscape mode.
    i tried this way its works fine in portrait but app crashes in Landscape. please how can i get rid of this crash

  3. hey! thx for the good tutorial, what name I can show in the image, if I take the image from gallery, I put an Unique Id but it looks awful

  4. I’ve followed your tutorial but for some reason the “retake’ and ‘use photo’ links aren’t working at all when I call the camera. Have you encountered this?

  5. Image is not changing and always show following error in console after selecting image swift. I am using Xcode 9.

    ” [discovery] errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 “query cancelled” UserInfo={NSLocalizedDescription=query cancelled}. “

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