Swift Swift: Using the UIImagePickerController for a Camera and Photo Library.

[Updated to Swift 2.0/iOS9 09/29/2015 SJL]

Screenshot 2014-12-04 07.15.31Almost 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

I’m going to set this up universally.  We’ll be talking about some of the issues with camera on both iPad and iPhone.  As a universal app we will be using auto layout. 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.

Go to the storyboard and check that the size classes say w:any h:any. Change the background color to black.

If you are not familiar with the auto layout icons, here is a guide to help you as we go through setting up the app.

autolayout buttons

We will put a photo into an UIImageView when we first  use the image.  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.

Screenshot 2014-12-03 08.47.26

Drag out the pizza to the view and drop it into the view controller. Click the pin button. Click on all the i-beams, then change numbers like the photo below:

Screenshot 2014-12-03 05.19.18

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. Also make sure you select Update Frames: Items of New Constraints towards the bottom. If you don’t, you will need to click the resolver and select Update Frame from the upper part of the popup. 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 onto the storyboard. Set the background property to a gray color with a 65% alpha.  Center the label. Change the text of the label to read Pizza Cam!!!.  Select the label and then pin the top left and right sides, but not the bottom, like this:

Screenshot 2014-12-03 05.17.44

Make  sure you select Update Frames: Items of New Constraints towards the bottom before adding the three constraints.

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

Screenshot 2014-12-03 05.18.23

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:

Screenshot 2014-12-03 14.59.18

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 popover. 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. Control-drag from the image view 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()
        // Do any additional setup after loading the view, typically from a nib.
        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 //2
picker.sourceType = .PhotoLibrary //3
presentViewController(picker, animated: true, completion: nil)//4
}

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, and line four we use presentViewController to present the picker in a default full screen modal.

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

func imagePickerControllerDidCancel(picker: UIImagePickerController) {
    dismissViewControllerAnimated(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
    dismissViewControllerAnimated(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.

Running as an iPhone and iPad app: Using Popovers

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

Screenshot 2014-12-04 06.04.04

Tap the library bar button. You will get an alert:

Screenshot 2014-12-04 06.04.15

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.  Tap OK.  We get our Image picker,

Screenshot 2014-12-04 06.04.28Screenshot 2014-12-04 06.04.41

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

Screenshot 2014-12-04 06.36.23

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

Screenshot 2014-12-04 06.11.38

Which looks fine, except Apple doesn’t like it. They want you to use a  popover.  If you present this as a modal using presentViewController in anything before iOS 8 , you  will get an exception and crash the app — Apple forces you to use a popover. All that changed in iOS 8 when modals and popovers merged into presentViewController.  The class documentation still notes that popovers are required for accessing the photo library on iPads when using the newly deprecated way of presenting a popover. I cannot find the correct way in the Human Interface Guide, but I think it is still good style to use a popover.   Fortunately in iOS 8, it is very easy to get the popover to work. Change this in photoFromLibrary

presentViewController(picker,
    animated: true,
    completion: nil)//4

to this:

picker.modalPresentationStyle = .Popover
presentViewController(picker,
     animated: true, completion: nil)//4
picker.popoverPresentationController?.barButtonItem = sender

Line 1 selects a presentation style of a popover. We then present the popover, and set 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.

Screenshot 2014-12-04 06.20.00 Screenshot 2014-12-04 06.20.28

I don’t know if using a full Screen modal will cause an app to be rejected, but since it is not hard to add, and presentViewController will present the right controller for the space available, this is probably a good idea — especially since it solves lots of problems with the iPhone 6 Plus models and multitasking, which can use both popovers and modals depending on circumstances.

Screenshot 2014-12-04 07.15.31

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

Adding a Camera: Where Simulators Fear to Tread.

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
presentViewController(picker,
    animated: true, 
    completion: nil)
}

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

If you build and run this in the simulator, you will crash.

SwiftPizzaCam[1649:42600] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Source type 1 not available'

In order to test and use camera code you have to use a real connected device. I connected my iPad mini and ran the code. When I pressed the photo button, I again get the message about allowing access.

Photo Dec 04, 7 29 44 AM

Tapping OK, I get the camera:

Photo Dec 04, 7 30 07 AM

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

Photo Dec 04, 7 30 35 AM

The app returns with my photo.

Photo Dec 04, 7 30 22 AM

This all works, but we really want to prevent that crash. Since we are using iOS 9, we don’t have to code so much as earlier versions due to a truth about our hardware: All the hardware that anything above iOS 8 works with has a rear camera. We just have to look for a rear camera. One way to do that is with a UIImagePickerController class method availableCaptureModesForCameraDevice. If nil, there is no hardware. Change the method to this:

@IBAction func shootPhoto(sender: UIBarButtonItem) {
    if UIImagePickerController.availableCaptureModesForCameraDevice(.Rear) != nil {
        picker.allowsEditing = false
        picker.sourceType = UIImagePickerControllerSourceType.Camera
        picker.cameraCaptureMode = .Photo
        presentViewController(picker, animated: true, completion: nil)
        } else {
            noCamera()
        }
}

Line 2 checks if there are any devices. If there is, run the camera. If not, execute our handler code noCamera. For that handler 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)
    presentViewController(alertVC,
        animated: true,
        completion: nil)
}

Line 2 makes an alert with the proper message. We add an OK action button in lines 3 and 4, then present the alert as a modal in line 5. With this code, if you run in the simulator, you get this when you attempt to take a picture:

Screenshot 2014-12-04 09.05.26 copy

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. We’ll have posts about individual concepts, like a post on  UIScrollView and then a post on how to add it to the camera app.

The Whole Code

//
//  ViewController.swift
//  SwiftPizzaCam
//
//  Created by Steven Lipton on 12/3/14.
//  Copyright (c) 2014 Steven Lipton. All rights reserved.
//
// Basic a camera app that takes pictures and grabs them for a background from the photo library

import UIKit

class ViewController: UIViewController,
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate {

    @IBOutlet weak var myImageView: UIImageView!
    let picker = UIImagePickerController()   //our controller.
                                             //Memory will be conserved a bit if you place this in the actions.
                                            // I did this to make code a bit more streamlined

    //MARK: - Methods
// An alert method using the new iOS 8 UIAlertController instead of the deprecated UIAlertview
// make the alert with the preferredstyle .Alert, make necessary actions, and then add the actions.
// add to the handler a closure if you want the action to do anything. 

    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)
        presentViewController(
            alertVC,
            animated: true,
            completion: nil)
    }

    //MARK: - Actions
//get a photo from the library. We present as a popover on iPad, and fullscreen on smaller devices.
    @IBAction func photoFromLibrary(sender: UIBarButtonItem) {
        picker.allowsEditing = false //2
        picker.sourceType = .PhotoLibrary //3
        picker.modalPresentationStyle = .Popover
        presentViewController(picker, 
            animated: true, 
            completion: nil)//4
        picker.popoverPresentationController?.barButtonItem = sender
    }

//take a picture, check if we have a camera first.
    @IBAction func shootPhoto(sender: UIBarButtonItem) {
        if UIImagePickerController.availableCaptureModesForCameraDevice(.Rear) != nil {
        picker.allowsEditing = false
        picker.sourceType = UIImagePickerControllerSourceType.Camera
        picker.cameraCaptureMode = .Photo
        picker.modalPresentationStyle = .FullScreen
        presentViewController(picker,
            animated: true,
            completion: nil)
        } else {
            noCamera()
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        picker.delegate = self   //the required delegate to get a photo back to the app.
    }
    //MARK: - Delegates
//What to do when the picker returns with a photo
    func imagePickerController(
        picker: UIImagePickerController, 
        didFinishPickingMediaWithInfo info: [String : AnyObject]) 
    {
        var chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage //2
        myImageView.contentMode = .ScaleAspectFit //3
        myImageView.image = chosenImage //4
        dismissViewControllerAnimated(true, completion: nil) //5
    }
//What to do if the image picker cancels.
    func imagePickerControllerDidCancel(picker: UIImagePickerController) {
        dismissViewControllerAnimated(true,
           completion: nil)
    }
}

52 thoughts on “Swift Swift: Using the UIImagePickerController for a Camera and Photo Library.”

  1. Hitting no for privacy settings for accessing the camera or library:

    For check camera:

    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if(status == AVAuthorizationStatusAuthorized) {
    // authorized
    } else if(status == AVAuthorizationStatusDenied){
    // denied
    } else if(status == AVAuthorizationStatusRestricted){
    // restricted
    } else if(status == AVAuthorizationStatusNotDetermined){
    // not determined
    [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
    if(granted){
    NSLog(@”Granted access”);
    } else {
    NSLog(@”Not granted access”);
    }
    }];
    }

    For check photos:

    if([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusNotDetermined) {
    // to do what you need
    }
    else if([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusRestricted) {
    NSLog(@”RESTRICTED”);
    }
    else if([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusDenied) {
    [[UIAlertView alertViewWithTitle:@”Please allow photos access.”
    message:@”1: Open Settings\n2: Tap Privacy\n3: Tap Photos\n4: Set Smooch to On”
    cancelButtonTitle:@”Ok” otherButtonTitles:@[@”Settings”]
    blockCompleteForClick:^(UIAlertView *alertView, NSInteger buttonIndex) {

    if (buttonIndex != 0) {
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }

    } ] show];
    }
    else if([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusAuthorized) {
    // to do what you need
    }

    You can change system privacy alerts, use keys: Privacy-Camera Usage Description, Privacy-Photo Library Usage Description

      1. That is a Swift 1.2 thing. The code is still Swift 1.1. Thanks for the catch. With 150 posts to update, I still hadn’t done this one yet. (let alone for Swift 2.0). I updated it.

  2. Steven, great tutorial. I’ve noticed that if I click “Libary” and load an image, and THEN click the Camera button to run the camera, the program crashes. In order to fix it, I set…
    picker.modalPresentationStyle = .FullScreen
    …in the shootPhoto function.
    Seems that if you call Library first, then picker.modalPresentationStyle gets set to .Popup, which generates an error when you try to use the camera.

    1. Scott, I didn’t read you not closely enough or I would have noticed, the problem. yes it will crash. Adding .Fullsheet kills the whole point of getting popovers sometimes and modals others. Besides doing so will get an app rejected for an ipad. Appke requires photo libraries to be a popover for iPads. You’ve run up against the one popover rule. Before iOS8 Apple strongly recommended in the style guide not to have two popovers open at the same time. Due to class sizes, Apple made this a fatal error instead. I Need to add some code here to close one popover if the other is open. I’ll shortly talk about it there when I make this fix, but you can find a full treatment of the topic in my book Swift Swift View Controllers.

      thanks for the catch!!

  3. Under the heading ‘Wire Up the Outlets’ The sentence “Control-drag from the image picker and make an outlet called myImagePicker.” I presume this is referring to the imageView?

      1. Happy to help. Your tutorials are fast becoming my first stop site while learning iOS and swift.

  4. Hi Steven:
    i wrote the code which are quite similar to yours and tested with ipad mini (xcode 7) , just one thing i had been scratching my head how to make the ipad mini camera in portrait mode, as in your post above, the picture right below “Tapping OK, I get the camera:”, show the camera in landscape mode. May I know how to code it and lock it in Portrait Mode?

    Thanks and Regards

    Simon

    1. I take back what I said in my other post. UIImagePickerController only works in one orientation on devices. The docs say iPhone only works in portrait and while it doesn’t say for iPad, I assume that landscape is its default orientation. You have to go to AVfoundation see Media Capture and Access to Camera in Apple’s AVFoundation Programming Guide. I currently do not have anything written about AVFoundation, I’m planning on January 2016 to hit that topic.

    1. Portrait should work just fine locked or unlocked. For iPad, Apple strongly recommends presenting as a .Fullscreen just like a phone, so in short exactly like a phone.

      See my other comment– this is not quite true. You’ll need AVFoundation.

    1. The dictionary object from in the UIImagePickerControllerDelegate method imagePickerController:didFinishPickingMediaWithInfo: contains an key UIImagePickerControllerMediaURL that returns the NSURL of the image.
      See https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImagePickerControllerDelegate_Protocol/index.html#//apple_ref/doc/constant_group/Editing_Information_Keys For the other keys you can use.

      That’s about all I can help you with.

    1. Bad news. UIImagePickerController is limited in orientation for the camera. Only portrait for iPhone and only Landscape for iPad. You have to use AVFoundation to do that. I haven’t written anything about AVFoundation yet. So I’m not much help.

    1. Check that delegate = self in ViewDidLoad and that you connected the outlets properly. You can check if its the delegate ny setting a breakpoint on the line that starts var chosenImage. If you can run the code without the program breaking at that line, the delegate is not set properly.

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