A reusable picture picker class for iOS
So on this tutorial we will create a reusable class constructed on prime of UIKit with a purpose to make picture choice extra nice in your apps, everyhing written in Swift 5.
This text was impressed by my earlier try to unravel the picture selecting difficulty in a protocol oriented means, however that article is these days a bit bit obsolated, plus I woundn’t use that method anymore.
Individuals at all times be taught from the previous, so as an alternative of utilizing a protocol oriented method, 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 deal with selecting edited photographs, if you would like to make use of reside pictures or motion pictures, you possibly can at all times 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 possibly can obtain a whole instance venture with video selecting as nicely from The.Swift.Dev. tutorials repository.
Privateness first!
These days privateness issues lots, so you need to 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 knowledge, you need to present a proof message for the person (and for Apple) why the app is requesting digital camera & photograph library entry. The NSCameraUsageDescription
is for digital camera and NSPhotoLibraryUsageDescription
secret’s for photograph library entry. Each values ought to be a simple 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 pictures.</string>
Clearly if you would like to make use of pictures instantly taken from the digital camera, however you do not wish 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. Principally 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 = .digital camera
Permits enhancing is a flag that signifies if the resizing & cropping interface ought to be introduced after deciding on & 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 sorts obtainable: photographs and flicks. You may get the obtainable media kind strings for every supply kind 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 photograph library. The .photoLibrary
enum case will provide you with full entry, however you possibly can restrict the choice scope just for the digital camera roll for those who select .savedPhotosAlbum
.
The delegate ought to implement each the UIImagePickerControllerDelegate
and the UINavigationControllerDelegate
protocols, nonetheless normally my navigation controller delegate is simply an empty implementation. In the event you want additional navigation associated logic, you would possibly must create a couple of strategies there as nicely.
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 kind: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(kind) else {
return nil
}
return UIAlertAction(title: title, model: .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: .digital camera, title: "Take photograph") {
alertController.addAction(motion)
}
if let motion = self.motion(for: .savedPhotosAlbum, title: "Digicam roll") {
alertController.addAction(motion)
}
if let motion = self.motion(for: .photoLibrary, title: "Picture library") {
alertController.addAction(motion)
}
alertController.addAction(UIAlertAction(title: "Cancel", model: .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 {
}
In the event you needn’t choose from a supply kind, issues are fairly simple, you possibly can merely current your picker view controller, deal with all the pieces within the delegate and you might be executed. Nevertheless, if you should select from an enter supply, that includes a bit bit extra logic, particularly on iPads. 📱
I am utilizing a UIAlertController
with a purpose to compose a supply kind choice dialog. I am making an attempt so as to add 3 actions (based mostly on the selecting supply kind), however provided that the supply kind is obtainable on that given system (eg. .digital camera
just isn’t obtainable within the simulator). You’ll be able to verify availability by: 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 technique. It is normally sufficient to set the sourceView and the sourceRect properties, however it’s also possible to customise arrow instructions. ⬅️➡️⬆️⬇️
It is at all times your activity to verify if the system 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! 🧀
Methods to use the picture picker class?
Properly, 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’s going to be capable to current the UIImagePickerController
on prime of that. I separated the delegate from the presentation controller, as a result of typically 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 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 instances you would possibly want a couple of additonal information from the picture picker.
If you wish to take this method one step additional, you possibly can 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 & methods!