In the chapter entitled Accessing the iOS 17 Camera and Photo Library, we looked in some detail at the steps necessary to provide access to the iOS camera and photo libraries in an iOS app. This chapter aims to build on this knowledge by working through an example iOS app designed to access the device’s camera and photo libraries.
An Overview of the App
The app user interface for this example will consist of an image view and a toolbar containing two buttons. When touched by the user, the first button will display the camera to the user and allow a photograph to be taken, which will subsequently be displayed in the image view. The second button will provide access to the camera roll, where the user may select an existing photo image. In the case of a new image taken with the camera, this will be saved to the camera roll.
Since we will cover the playback of video in the next chapter (iOS 17 Video Playback using AVPlayer and AVPlayerViewController), the camera roll and camera will be restricted to still images in this example. Adding video support to this app is left as an exercise for the reader.
Creating the Camera Project
Begin the project by launching Xcode and creating a new project using the iOS App template with the Swift and Storyboard options selected, entering Camera as the product name.
Designing the User Interface
The next step in this tutorial is to design the user interface. This simple user interface consists of an image view, a toolbar, and two bar button items. Select the Main.storyboard file and drag components from the Library onto the view. Position and size the components and set the text on the bar button items, so the user interface resembles Figure 72-1.
In terms of Auto Layout constraints, begin by selecting the toolbar view and clicking on the Add New Constraints menu in the toolbar located in the lower right-hand corner of the storyboard canvas and establish Spacing to nearest neighbor constraints on the bottom and two side edges of the view with the Constrain to margins option switched off. Also, enable the height constraint option to be set to the current value.
Next, select the Image View object and, once again using the Add New Constraints menu, establish Spacing to nearest neighbor constraints on all four sides of the view, with the Constrain to margins option enabled.
Finally, with the Image View still selected, display the Attributes Inspector panel and change the Content Mode attribute to Aspect Fit.
Select the image view object in the view canvas and display the Assistant Editor panel. Ctrl-click on the image view object and drag it to a position just below the class declaration line in the Assistant Editor. Release the line and, in the resulting connection dialog, establish an outlet connection named imageView.
With the Assistant Editor still visible, establish action connections for the two buttons to methods named useCamera and useCameraRoll, respectively (keeping in mind that it may be necessary to click twice on each button to select it since the first click will typically select the toolbar parent object).
Close the Assistant Editor, select the ViewController.swift file and modify it further to add import and delegate protocol declarations together with a Boolean property declaration that will be required later in the chapter:
import UIKit
import MobileCoreServices
import UniformTypeIdentifiers
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@IBOutlet weak var imageView: UIImageView!
var newMedia: Bool?
.
.
Code language: Swift (swift)
Implementing the Action Methods
The useCamera and useCameraRoll action methods now need to be implemented. The useCamera method first needs to check that the device on which the app is running has a camera. It then needs to create a UIImagePickerController instance, assign the cameraViewController as the delegate for the object and define the media source as the camera. Since we do not plan on handling videos, the supported media types property is set to images only. Finally, the camera interface will be displayed. The last task is to set the newMedia flag to true to indicate that the image is new and is not an existing image from the camera roll. Bringing all these requirements together gives us the following useCamera method:
@IBAction func useCamera(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(
UIImagePickerController.SourceType.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType =
UIImagePickerController.SourceType.camera
imagePicker.mediaTypes = [UTType.image.identifier]
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true,
completion: nil)
newMedia = true
}
}
Code language: Swift (swift)
The useCameraRoll method is remarkably similar to the previous method, with the exception that the source of the image is declared to be UIImagePickerController.SourceType.photoLibrary and the newMedia flag is set to false (since the photo is already in the library, we don’t need to save it again):
@IBAction func useCameraRoll(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(
UIImagePickerController.SourceType.savedPhotosAlbum) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType =
UIImagePickerController.SourceType.photoLibrary
imagePicker.mediaTypes = [UTType.image.identifier]
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true,
completion: nil)
newMedia = false
}
}
Code language: Swift (swift)
Writing the Delegate Methods
As described in Accessing the iOS 17 Camera and Photo Library, some delegate methods must be implemented to fully implement an instance of the image picker controller delegate protocol. The most important method is didFinishPickingMediaWithInfo which is called when the user has finished taking or selecting an image. The code for this method in our example reads as follows:
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let mediaType = info[UIImagePickerController.InfoKey.mediaType]
as! NSString
self.dismiss(animated: true, completion: nil)
if mediaType.isEqual(to: UTType.image.identifier) {
let image = info[UIImagePickerController.InfoKey.originalImage]
as! UIImage
imageView.image = image
if (newMedia == true) {
UIImageWriteToSavedPhotosAlbum(image, self,
#selector(ViewController.image(
image:didFinishSavingWithError:contextInfo:)), nil)
} else if mediaType.isEqual(to: UTType.image.identifier) {
// Code to support video here
}
}
}
@objc func image(image: UIImage, didFinishSavingWithError error: NSErrorPointer, contextInfo:UnsafeRawPointer) {
if error != nil {
let alert = UIAlertController(title: "Save Failed",
message: "Failed to save image",
preferredStyle: UIAlertController.Style.alert)
let cancelAction = UIAlertAction(title: "OK",
style: .cancel, handler: nil)
alert.addAction(cancelAction)
self.present(alert, animated: true,
completion: nil)
}
}
Code language: Swift (swift)
The code in this delegate method dismisses the image picker view and identifies the type of media passed from the image picker controller. If it is an image, it is displayed on the view image object of the user interface. If this is a new image, it is saved to the camera roll. When the save operation is complete, the didFinishedSavingWithError method is configured to be called. If an error occurs, it is reported to the user via an alert box.
It is also necessary to implement the imagePickerControllerDidCancel delegate method, which is called if the user cancels the image picker session without taking a picture or making an image selection. In most cases, all this method needs to do is dismiss the image picker:
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
Code language: Swift (swift)
Seeking Camera and Photo Library Access
Access to both the camera and the photos stored on the device requires authorization from the user. This involves declaring three usage keys within the project Info.plist file.
To add these entries, select the Camera entry at the top of the Project navigator panel and select the Info tab in the main panel. Next, click on the + button contained with the last line of properties in the Custom iOS Target Properties section. Then, select the Privacy – Camera Usage Description item from the resulting menu. Once the key has been added, double-click in the corresponding value column and enter the following text:
This app allows you to take photos and store them in the photo library.
Code language: plaintext (plaintext)
Repeat this step, this time adding a Privacy – Photo Library Usage Description key set to the following string value:
This app allows you to take photos and store them in the photo library.
Code language: plaintext (plaintext)
Finally, add another key to the list, this time selecting the Privacy – Photo Library Additions Usage Description option from the drop-down menu. This permission is required to allow the app to add new photos to the user’s photo library. Once the key has been selected, enter text similar to the following into the value field:
This app saves photos only to your photo library. This app saves photos only to your photo library.
Building and Running the App
To experience the full functionality of this app, you will need to install it on a physical iOS device.
Click on the Xcode run button to launch the app. Once the app loads, select the Camera button to launch the camera interface and tap the Allow button when prompted to provide access to the camera and take a photo. Next, tap the Use Photo button, after which your photo should appear in the image view object of our app user interface:
Selecting the Camera Roll button will provide access to the camera roll and photo stream on the device where an image selection can be made:
Summary
This chapter has provided a practical example of how the UIImagePickerController class can access the camera and photo library within an iOS app. The example also demonstrated the need to configure appropriate privacy usage keys to seek permission from the user to access the device camera and search and add images to the photo library.