← Back to Blog

Swift Camera Basics: Image Pickers and Action Sheets

mobile
tutorial

By Kevin Hou

7 minute read

I am designing and implementing a profile screen into Breathometer’s new app and one of the key components of many profile screens is a profile picture. I’m continuing to add to my toolbox of Swift skills so I set about learning how to get pictures from either the camera app or phone’s photo library.

We need to build some basic frontend components so that we can have control over our code: ImageView: A view that our image from the camera/photo library will populate Button: To control when we want to upload the image

Setup View Controller Class

In order to use photos from the device, you must use the UIImagePickerControllerDelegate and the UINavigationControllerDelegate. These will give you the necessary functions to be able to retrieve your first image. The UIImagePickerControllerDelegate is responsible for initiating an image picker controller which then piggybacks with the UINavigationControllerDelegate that allows you to present the image picker using presentViewController() and dismissViewControllerAnimated(). Your view controller class should look like this:

1class ProfileScreen: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 2 override func viewDidLoad() { 3 super.viewDidLoad() 4 } 5} 6

Connect Storyboard to Code

Create an outlet for the imageView and an action outlet for the button:

1@IBOutlet weak var profilePictureImageView: UIImageView! // Image view we want to display the image in 2@IBAction func selectProfilePicture(sender: AnyObject) { 3} 4

Setup Image Picker

Initialize the UIImagePickerController under the UIImageView outlet we created earlier:

1let imagePicker = UIImagePickerController() // Initialize an image picker view controller type 2

Now we can create a function that will serve up the image picker. It accepts a sourceType as an input so that the image picker knows whether to show the actual camera interface or the photo library interface. There are three main parts in displaying an image picker: editing, source type, and presenting. Source type can be from the camera, the camera roll, or the photo library. The presentation component is what actually presents the imagePicker controller.

1func pickImage(sourceType: UIImagePickerControllerSourceType) { 2 imagePicker.allowsEditing = false // Prevent editing 3 4 // Three sources: .PhotoLibrary, .Camera, .SavedPhotosAlbum 5 self.imagePicker.sourceType = sourceType // Type of image selection 6 7 // Presenting image picker view controller on top of stack 8 self.presentViewController(self.imagePicker, animated: true, completion: { 9 print("Opening image picker view controller") 10 }) 11} 12

Create Action Sheet

We want the user to be able to specify if they want to use their camera or the photo library to select an image. We give them this option by creating an action sheet. An action sheet looks like this:

swift action sheet

Because we want this action sheet to appear when the button is pressed, we place this bit of code in the action outlet. The code is straightforward and intuitive:

1let imageSourcePicker = UIAlertController(title: "Select Profile Picture", message: "Please select an image picker method", preferredStyle: .ActionSheet) // Initialize action sheet type 2 3let cameraAction = UIAlertAction(title: "Take a picture", style: .Default, handler: { action in 4 self.pickImage(.Camera) // Presents picker 5}) 6 7let cameraRoll = UIAlertAction(title: "Choose from camera roll", style: .Default, handler: { action in 8 self.pickImage(.PhotoLibrary) // Presents picker 9}) 10 11// Add actions 12imageSourcePicker.addAction(cameraAction) 13imageSourcePicker.addAction(cameraRoll) 14 15presentViewController(imageSourcePicker, animated: true, completion: nil) 16

Setup the UIImagePickerControllerDelegate Methods

We must set the imagePicker delegate to self in viewDidLoad():

1imagePicker.delegate = self // States that this view controller will also handle the events 2

Now we can write the methods. There are two image picker functions are essential:

1imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) 2

This function is triggered when you return a valid image. The selected image is passed in as part of the info object.

1func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { 2 if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage { 3 // If returned a valid image 4 profilePictureImageView.contentMode = .ScaleAspectFit // Set content setting 5 profilePictureImageView.image = pickedImage // Set image 6 } 7 8 // Dismiss the image picker controller 9 dismissViewControllerAnimated(true, completion: { 10 print("Dismissed image picker controller") 11 }) 12} 13 14imagePickerControllerDidCancel(picker: UIImagePickerController) 15This is triggered when the image selection flow is cancelled 16func imagePickerControllerDidCancel(picker: UIImagePickerController) { 17 // Dismiss the image picker controller 18 dismissViewControllerAnimated(true, completion: { 19 print("Cancelled image picker flow") 20 21 }) 22} 23

Final Code

The complete source code looks like this:

1// Created by Kevin 2// Copyright © 2016 KevinHou. All rights reserved. 3// 4 5import Foundation 6import UIKit 7 8class ProfileScreen: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 9 10 // Image picker controller delegate allows you to select an image from the camera or the photo library 11 // Navigation controller delegate allows image picker to appear and dissapear like a normal view controller 12 13 @IBOutlet weak var profilePictureImageView: UIImageView! // Image view we want to display the image in 14 15 let imagePicker = UIImagePickerController() // Initialize an image picker view controller type 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 20 imagePicker.delegate = self // States that this view controller will also handle the events 21 } 22 23 @IBAction func selectProfilePicture(sender: AnyObject) { 24 25 let imageSourcePicker = UIAlertController(title: "Select Profile Picture", message: "Please select an image picker method", preferredStyle: .ActionSheet) // Initialize action sheet type 26 27 let cameraAction = UIAlertAction(title: "Take a picture", style: .Default, handler: { action in 28 self.pickImage(.Camera) // Presents picker 29 }) 30 31 let cameraRoll = UIAlertAction(title: "Choose from camera roll", style: .Default, handler: { action in 32 self.pickImage(.PhotoLibrary) // Presents picker 33 }) 34 35 // Add actions 36 imageSourcePicker.addAction(cameraAction) 37 imageSourcePicker.addAction(cameraRoll) 38 39 presentViewController(imageSourcePicker, animated: true, completion: nil) 40 41 } 42 43 func pickImage(sourceType: UIImagePickerControllerSourceType) { 44 imagePicker.allowsEditing = false // Prevent editing 45 46 // Three sources: .PhotoLibrary, .Camera, .SavedPhotosAlbum 47 self.imagePicker.sourceType = sourceType // Type of image selection 48 49 // Presenting image picker view controller on top of stack 50 self.presentViewController(self.imagePicker, animated: true, completion: { 51 print("Opening image picker view controller") 52 }) 53 } 54 55 56 // MARK: - UIImagePickerControllerDelegate Methods 57 58 func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { 59 if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage { 60 // If returned a valid image 61 profilePictureImageView.contentMode = .ScaleAspectFit // Set content setting 62 profilePictureImageView.image = pickedImage // Set image 63 } 64 65 // Dismiss the image picker view controller 66 dismissViewControllerAnimated(true, completion: { 67 print("Dismissed image picker view controller") 68 }) 69 } 70 71 func imagePickerControllerDidCancel(picker: UIImagePickerController) { 72 // Dismiss the image picker view controller 73 dismissViewControllerAnimated(true, completion: { 74 print("Cancelled: Dismissed image picker view controller") 75 }) 76 } 77 78 79} 80

Hope you found this helpful! You can find the source code on GitHub.

Another Example of an Action Sheet

Here's another example of two different types of action sheets in case the first example was difficult to grasp.

1Text Input Dialogue: 2// Create text modal for adding an event name prediction 3let newCategoryAlert = UIAlertController(title: "Enter Location", message: "Manually add a location suggestion", preferredStyle: .alert) 4 5// Add text field item 6newCategoryAlert.addTextField { (textField) in 7 textField.text = "" // No placeholder 8 textField.autocapitalizationType = UITextAutocapitalizationType.words // Capitalization rules 9} 10 11// Add cancel action 12newCategoryAlert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil)) // No action if cancelled 13 14// Add submit action 15newCategoryAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak newCategoryAlert] (_) in 16 // Get text field content 17 let textField = newCategoryAlert?.textFields![0] // Force unwrapping because we know it exists 18 19 self.currentCategory.locationFreq[(textField?.text)!] = 0 // New event name frequency entry with frequency of 0 20 21 // Update category data with new markov model 22 DataManager.updateOneCategory(with: self.currentCategory, index: self.selectedIndex) 23 24 // Refresh the table view on this page 25 self.refreshData() 26})) 27 28self.present(newCategoryAlert, animated: true, completion: nil) // Present the alert 29 30// Regular Action Sheet 31let actionSheet = UIAlertController(title: "Reset Predictions", message: "Are you sure you want to reset predictions? This cannot be undone.", preferredStyle: .actionSheet) // Create alert action sheet 32 33// Create actions 34let resetAction: UIAlertAction = UIAlertAction(title: "Reset Predictions", style: .default, handler: { (alert: UIAlertAction!) -> Void in 35 // User pressed reset all predictions 36 print("Reseting predictions") 37 self.currentCategory = Category(name: self.currentCategory.name, eventNameFreq: [ : ], locationFreq: [ : ]) // Reset predictions data 38 DataManager.updateOneCategory(with: self.currentCategory, index: self.selectedIndex) 39}) 40let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { (alert: UIAlertAction!) -> Void in 41 // User pressed cancel 42}) 43 44// Add actions 45actionSheet.addAction(resetAction) 46actionSheet.addAction(cancelAction) 47 48self.present(actionSheet, animated: true, completion: nil) // Present action sheet to user 49 50

Hope you found this helpful!