A reusable picture picker class for iOS
So on this tutorial we’ll create a reusable class constructed on high of UIKit as a way to make picture choice extra nice to your apps, everyhing written in Swift 5.
This text was impressed by my earlier try to resolve the picture choosing challenge in a protocol oriented approach, however that article is these days slightly bit obsolated, plus I woundn’t use that method anymore.
Folks at all times be taught from the previous, so as an alternative of utilizing a protocol oriented strategy, this time I am going to merely go along with an ImagePicker class. No singletons, no additional library, only a small helper class that may be instantiated within the acceptable place, to do it is job. 🌄
I am solely going to give attention to choosing edited pictures, if you would like to make use of reside photographs or motion pictures, you’ll be able to at all times customise the ImagePicker class, or create an summary one and implement subclasses for every media sort. I might accomplish that too. 😅
So let’s dive in, right here is my fundamental implementation, however you’ll be able to obtain an entire instance venture with video choosing as effectively from The.Swift.Dev. tutorials repository.
Privateness first!
These days privateness issues rather a lot, so you must add two vital keys to your purposes Data.plist
file, in any other case you will find yourself with a horrible crash! ⚠️
Because you’d wish to get some personal information, you must present an evidence message for the person (and for Apple) why the app is requesting digital camera & picture library entry. The NSCameraUsageDescription
is for digital camera and NSPhotoLibraryUsageDescription
key’s for picture library entry. Each values needs to be an easy string that’ll clarify the person why you want his/her nude footage. Pricacy is vital! 🔒
<key>NSCameraUsageDescription</key>
<string>This app needs to take footage.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs to make use of your photographs.</string>
Clearly if you would like to make use of photographs instantly taken from the digital camera, however you do not need to entry the picture library, you simply have so as to add the right key. That is it now we’re able to do some precise coding. ⌨️
The anatomy of UIImagePickerController
The anatomy of a UIPickerController is kind of easy. Principally it is a common view controller, you simply should set just a few additional properties to make it work.
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.allowsEditing = true
pickerController.mediaTypes = ["public.image", "public.movie"]
pickerController.sourceType = .digital camera
Permits modifying is a flag that signifies if the resizing & cropping interface needs to be introduced after deciding on & taking an image, if true it is best to use the .editedImage
as an alternative of the .originalImage
key – contained in the picker delegate – to get the right picture from the picture information dictionary.
There are mainly two sorts of media sorts obtainable: pictures and films. You may get the obtainable media sort strings for every supply sort by calling a category technique on the picker: UIImagePickerController.availableMediaTypes(for: .digital camera)
.
There are 3 obtainable supply sorts: .digital camera
, which is the digital camera, and there are two different choices to get footage from the picture library. The .photoLibrary
enum case offers you full entry, however you’ll be able to restrict the choice scope just for the digital camera roll in case you select .savedPhotosAlbum
.
The delegate ought to implement each the UIImagePickerControllerDelegate
and the UINavigationControllerDelegate
protocols, nonetheless often my navigation controller delegate is simply an empty implementation. When you want additional navigation associated logic, you would possibly have to create just a few strategies there as effectively.
Awww, let’s simply put all the pieces collectively…
import UIKit
public protocol ImagePickerDelegate: class {
func didSelect(picture: UIImage?)
}
open class ImagePicker: NSObject {
personal let pickerController: UIImagePickerController
personal weak var presentationController: UIViewController?
personal weak var delegate: ImagePickerDelegate?
public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
self.pickerController = UIImagePickerController()
tremendous.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.mediaTypes = ["public.image"]
}
personal func motion(for sort: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(sort) else {
return nil
}
return UIAlertAction(title: title, fashion: .default) { [unowned self] _ in
self.pickerController.sourceType = sort
self.presentationController?.current(self.pickerController, animated: true)
}
}
public func current(from sourceView: UIView) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let motion = self.motion(for: .digital camera, title: "Take picture") {
alertController.addAction(motion)
}
if let motion = self.motion(for: .savedPhotosAlbum, title: "Digital camera roll") {
alertController.addAction(motion)
}
if let motion = self.motion(for: .photoLibrary, title: "Photograph library") {
alertController.addAction(motion)
}
alertController.addAction(UIAlertAction(title: "Cancel", fashion: .cancel, handler: nil))
if UIDevice.present.userInterfaceIdiom == .pad {
alertController.popoverPresentationController?.sourceView = sourceView
alertController.popoverPresentationController?.sourceRect = sourceView.bounds
alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
}
self.presentationController?.current(alertController, animated: true)
}
personal func pickerController(_ controller: UIImagePickerController, didSelect picture: UIImage?) {
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(picture: picture)
}
}
extension ImagePicker: UIImagePickerControllerDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(picker, didSelect: nil)
}
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo information: [UIImagePickerController.InfoKey: Any]) {
guard let picture = information[.editedImage] as? UIImage else {
return self.pickerController(picker, didSelect: nil)
}
self.pickerController(picker, didSelect: picture)
}
}
extension ImagePicker: UINavigationControllerDelegate {
}
When you need not choose from a supply sort, issues are fairly simple, you’ll be able to merely current your picker view controller, deal with all the pieces within the delegate and you’re executed. Nevertheless, if you must select from an enter supply, that entails slightly bit extra logic, particularly on iPads. 📱
I am utilizing a UIAlertController
as a way to compose a supply sort choice dialog. I am making an attempt so as to add 3 actions (based mostly on the choosing supply sort), however provided that the supply sort is out there on that given gadget (eg. .digital camera
will not be obtainable within the simulator). You possibly can test availability by way of: UIImagePickerController.isSourceTypeAvailable(sort)
.
Alert controllers wants just a few additional issues on iPads, that is why I am organising the popoverPresentationController properties within the current technique. It is often sufficient to set the sourceView and the sourceRect properties, however you may also customise arrow instructions. ⬅️➡️⬆️⬇️
It is at all times your process to test if the gadget is an iPad & set the right supply view & rect if it is wanted, in any other case your app will crash on iPads. One other factor is that you must dismiss the UIPickerViewController
after the picker did it is job! ⚠️
Time to say cheese! 🧀
The way to use the picture picker class?
Effectively, now you’re able to take some footage. I’ve made a easy view controller to indicate you an actual fast instance. You solely want a UIImageView
and a UIButton
.
Now that is the code for the pattern view controller. Nothing magical, I simply go the controller as a presentationController for the ImagePicker
so it’s going to be capable to current the UIImagePickerController
on high of that. I separated the delegate from the presentation controller, as a result of typically it comes useful. 🤷♂️
class ViewController: UIViewController {
@IBOutlet var imageView: UIImageView!
var imagePicker: ImagePicker!
override func viewDidLoad() {
tremendous.viewDidLoad()
self.imagePicker = ImagePicker(presentationController: self, delegate: self)
}
@IBAction func showImagePicker(_ sender: UIButton) {
self.imagePicker.current(from: sender)
}
}
extension ViewController: ImagePickerDelegate {
func didSelect(picture: UIImage?) {
self.imageView.picture = picture
}
}
The ImagePickerDelegate
delegate on this case is the simplest one I can think about. It simply provides the picked picture so that you’re prepared to make use of it. Nevertheless in some circumstances you would possibly want just a few additonal information from the picture picker.
If you wish to take this strategy one step additional, you’ll be able to create an summary class or a protocol that defines the essential performance and based mostly on which you can implement varied media picker controllers to suit your wants. Anyway, my level right here was to replace an outdated article, observe me & subscribe for extra UIKit ideas & tips!