Are you in search of a video picker as nicely? 🍿 Take a look at my one other submit about selecting & enjoying video information in iOS.
A reusable picture picker class for iOS
So on this tutorial we’ll create a reusable class constructed on high of UIKit to be able to make picture choice extra nice on your apps, all the things written in Swift 5.
This text was impressed by my earlier try to unravel the picture selecting difficulty in a protocol oriented manner, however that article is these days a little bit bit obsolated, plus I would not use that approach anymore.
Individuals all the time be taught from the previous, so as an alternative of utilizing a protocol oriented strategy, this time I will merely go together 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 concentrate on selecting edited photographs, if you would like to make use of stay images or films, you may all the time customise the ImagePicker class, or create an summary one and implement subclasses for every media kind. I would achieve this too. 😅
So let’s dive in, right here is my primary implementation, however you may obtain a whole instance challenge with video selecting as nicely from The.Swift.Dev. tutorials repository on GitHub.
Privateness first!
These days privateness issues so much, so you need to add two necessary keys to your functions Information.plist file, in any other case you may find yourself with a horrible crash! ⚠️
Because you’d wish to get some non-public information, you need to present an evidence message for the consumer (and for Apple) why the app is requesting digicam & photograph library entry. The NSCameraUsageDescription is for digicam and NSPhotoLibraryUsageDescription secret is for photograph library entry. Each values ought to be a simple string that’ll clarify the consumer why you want his/her nude footage. Privateness is necessary! 🔒
<key>NSCameraUsageDescription</key>
<string>This app desires to take footage.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app desires to make use of your images.</string>
Clearly if you would like to make use of images straight taken from the digicam, however you do not need to entry the photograph 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. Mainly it is a common view controller, you simply should set a couple of additional properties to make it work.
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.allowsEditing = true
pickerController.mediaTypes = ["public.image", "public.movie"]
pickerController.sourceType = .digicam
Permits modifying is a flag that signifies if the resizing & cropping interface ought to be offered after choosing & taking an image, if true you must 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 varieties obtainable: photographs and films. You may get the obtainable media kind strings for every supply kind by calling a category methodology on the picker:
UIImagePickerController.availableMediaTypes(
for: .digicam
)
There are 3 obtainable supply varieties: .digicam, which is the digicam, and there are two different choices to get footage from the photograph library. The .photoLibrary enum case offers you full entry, however you may restrict the choice scope just for the digicam roll if you happen to select .savedPhotosAlbum.
The delegate ought to implement each the UIImagePickerControllerDelegate and the UINavigationControllerDelegate protocols, nevertheless normally my navigation controller delegate is simply an empty implementation. When you want additional navigation associated logic, you may have to create a couple of strategies there as nicely.
Awww, let’s simply put all the things collectively…
import UIKit
public protocol ImagePickerDelegate: class {
func didSelect(picture: UIImage?)
}
open class ImagePicker: NSObject {
non-public let pickerController: UIImagePickerController
non-public weak var presentationController: UIViewController?
non-public 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"]
}
non-public func motion(for kind: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(kind) else {
return nil
}
return UIAlertAction(title: title, type: .default) { [unowned self] _ in
self.pickerController.sourceType = kind
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: .digicam, title: "Take photograph") {
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: "Picture library") {
alertController.addAction(motion)
}
alertController.addAction(UIAlertAction(title: "Cancel", type: .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)
}
non-public 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 needn’t choose from a supply kind, issues are fairly easy, you may merely current your picker view controller, deal with all the things within the delegate and you might be achieved. Nonetheless, if it’s essential to select from an enter supply, that entails a little bit bit extra logic, particularly on iPads. 📱
I am utilizing a UIAlertController to be able to compose a supply kind choice dialog. I am making an attempt so as to add 3 actions (primarily based on the selecting supply kind), however provided that the supply kind is on the market on that given gadget (e.g. .digicam shouldn’t be obtainable within the simulator). You’ll be able to test availability by means of: UIImagePickerController.isSourceTypeAvailable(kind).
Alert controllers wants a couple of additional issues on iPads, that is why I am establishing the popoverPresentationController properties within the current methodology. It is normally sufficient to set the sourceView and the sourceRect properties, however you may as well customise arrow instructions. ⬅️➡️⬆️⬇️
It is all the time 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 need to dismiss the UIPickerViewController after the picker did it is job! ⚠️
Time to say cheese! 🧀
The best way to use the picture picker class?
Effectively, now you might be able to take some footage. I’ve made a easy view controller to point out 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 move the controller as a presentationController for the ImagePicker so it will be capable to current the UIImagePickerController on high of that. I separated the delegate from the presentation controller, as a result of generally it comes helpful. 🤷♂️
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 most straightforward one I can think about. It simply offers the picked picture so that you’re prepared to make use of it. Nonetheless in some circumstances you may want a couple of more information from the picture picker.
If you wish to take this strategy one step additional, you may create an summary class or a protocol that defines the fundamental performance and primarily based on that you could implement numerous media picker controllers to suit your wants.
