Are you on the lookout for a video picker as effectively? 🍿 Take a look at my one other submit about selecting & taking part in video recordsdata in iOS.
A reusable picture picker class for iOS
So on this tutorial we will create a reusable class constructed on high of UIKit so as to make picture choice extra nice to your apps, every part written in Swift 5.
This text was impressed by my earlier try to resolve the picture selecting difficulty in a protocol oriented approach, however that article is these days a bit bit obsolated, plus I would not use that approach anymore.
Folks at all times study 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 further library, only a small helper class that may be instantiated within the applicable place, to do it is job. 🌄
I am solely going to deal with selecting edited photos, if you would like to make use of stay photographs or films, you may 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 fundamental implementation, however you may obtain a whole instance challenge with video selecting as effectively from The.Swift.Dev. tutorials repository on GitHub.
Privateness first!
These days privateness issues lots, so it’s important to add two necessary keys to your functions Data.plist
file, in any other case you may find yourself with a horrible crash! ⚠️
Because you’d wish to get some non-public knowledge, it’s important to present a proof message for the person (and for Apple) why the app is requesting digicam & picture library entry. The NSCameraUsageDescription
is for digicam and NSPhotoLibraryUsageDescription
secret’s for picture library entry. Each values must be a simple string that’ll clarify the person 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 photographs.</string>
Clearly if you would like to make use of photographs immediately taken from the digicam, however you do not need to entry the picture library, you simply have so as to add the correct 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 further 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 must be introduced after choosing & taking an image, if true it’s best to use the .editedImage as an alternative of the .originalImage key – contained in the picker delegate – to get the correct picture from the picture data dictionary.
There are mainly two sorts of media varieties out there: photos and flicks. You may get the out there media kind strings for every supply kind by calling a category methodology on the picker:
UIImagePickerController.availableMediaTypes(
for: .digicam
)
There are 3 out there supply varieties: .digicam, which is the digicam, and there are two different choices to get footage from the picture library. The .photoLibrary
enum case provides you with full entry, however you may restrict the choice scope just for the digicam roll should you 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. In the event you want further navigation associated logic, you would possibly must create a couple of strategies there as effectively.
Awww, let’s simply put every part 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, 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: .digicam, 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", 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)
}
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 data: [UIImagePickerController.InfoKey: Any]) {
guard let picture = data[.editedImage] as? UIImage else {
return self.pickerController(picker, didSelect: nil)
}
self.pickerController(picker, didSelect: picture)
}
}
extension ImagePicker: UINavigationControllerDelegate {
}
In the event you need not choose from a supply kind, issues are fairly simple, you may merely current your picker view controller, deal with every part within the delegate and you might be finished. Nevertheless, if you might want to select from an enter supply, that entails a bit bit extra logic, particularly on iPads. 📱
I am utilizing a UIAlertController
so as 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 on the market on that given machine (e.g. .digicam
shouldn’t be out there within the simulator). You may verify availability by: UIImagePickerController.isSourceTypeAvailable(kind).
Alert controllers wants a couple of further 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 also customise arrow instructions. ⬅️➡️⬆️⬇️
It is at all times your activity to verify if the machine is an iPad & set the correct supply view & rect if it is wanted, in any other case your app will crash on iPads. One other factor is that it’s important to dismiss the UIPickerViewController after the picker did it is job! ⚠️
Time to say cheese! 🧀
The way to use the picture picker class?
Nicely, now you might be 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 will have the ability to current the UIImagePickerController
on high of that. I separated the delegate from the presentation controller, as a result of generally 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 most straightforward 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 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 based mostly on that you may implement varied media picker controllers to suit your wants.