Taking and storing a picture

When you need a user to supply an image, they can do this by either selecting an image from their photo library, or by taking a picture with the camera. The UIImagePickerController supports both ways of picking an image. In this sample, you will learn how you can allow users to take an image using the camera. Changing the example to allow users to select an image from their photo library should be trivial, as long as you remember to add the Privacy - Photo Library Usage Description key to your Info.plist.

Add the following implementation for viewDidLoad() to the ImageViewController class:

override func viewDidLoad() {
  super.viewDidLoad()

  let imagePicker = UIImagePickerController()
  imagePicker.sourceType = .camera
  imagePicker.delegate = self
  present(imagePicker, animated: true, completion: nil)
}

The previous implementation creates an instance of the UIImagePickerController object, and configures it so that it uses the camera as the image source and presents it to the user. Note that the view controller is set as a delegate for the image picker. When the user has taken a picture, the image picker will notify its delegate about this, so that it can extract the image and use it. In this case, the image should be given the selectedImage label in the view controller so it can be shown in the image view, and saved when the user taps on the save button and the saveImage() method is called as a result.

Add the following extension to make ImageViewController conform to UIImagePickerControllerDelegate:

extension ImageViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true, completion: nil)

    guard let image = info[.originalImage] as? UIImage
      else { return }

    selectedImage = image
  }
}

Note that this extension also makes the image view controller conform to UINavigationControllerDelegate. The delegate property on the image picker controller requires all delegates to conform to both UINavigationControllerDelegate and UIImagePickerControllerDelegate.

When the user has taken a picture with the camera, imagePickerController(_: didFinishPickingMediaWithInfo) is called to notify the delegate about the photo that the user took. The first thing that the preceding code does is dismiss the picker, as it's no longer needed. The picture that the user just took is stored in the info dictionary as the original image. When the image is extracted from the dictionary, it is set as selectedImage.

To store the image, add the following implementation of saveImage():

@IBAction func saveImage() {
  guard let image = selectedImage
    else { return }

  UIImageWriteToSavedPhotosAlbum(image, self, #selector(didSaveImage(_:withError:contextInfo:)), nil)
}

@objc func didSaveImage(_ image: UIImage, withError error: Error?, contextInfo: UnsafeRawPointer) {
  guard error == nil
    else { return }

  presentAlertWithTitle("Success", message: "Image was saved succesfully")
}

The preceding code calls UIImageWriteToSavedPhotosAlbum(_:_:_:_) to store the image in the user's photo library. When the save operation completes, the didSaveImage(_:withError:contextInfo:) method will be called. If this method does not receive any errors, then the photo was successfully stored in the photo library and an alert is shown.

Allowing the user to take a picture by implementing UIImagePickerController is relatively straightforward, and it's a great way to implement a camera feature in your app without too much effort. Sometimes, you need more advanced access to the camera. In these cases, you can use AVFoundation to gain access to the raw video feed from the camera, as you will see next.