Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ImagePicker. Simple solution to use camera/photo library to pick an image
//
// AuthImagePickerSettings.swift
//
// Created by Marcelo Perretta on 18/05/2022.
//
import UIKit
import AVFoundation
import Photos
import PhotosUI
class AuthImagePickerSettings: NSObject {
@discardableResult
public static func authCamera(message: String, completion: @escaping () -> Void) -> Bool {
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .authorized:
completion()
return true
case .denied:
DispatchQueue.main.async {
presentAlert(message: message)
}
return false
case .restricted:
return false
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
completion()
}
}
default:
return false
}
return false
}
@discardableResult
public static func authPhotoLibrary(message: String, completion: @escaping () -> Void) -> Bool {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
completion()
return true
case .denied:
DispatchQueue.main.async {
presentAlert(message: message)
}
return false
case .restricted:
return false
case .notDetermined:
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
completion()
}
}
return true
case .limited:
if let rootVC = UIViewController.rootViewController {
if #available(iOS 14, *) {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: rootVC)
}
}
return true
default:
return false
}
}
static func presentAlert(message: String) {
let alertCtrl = UIAlertController(title: NSLocalizedString("Need to Authorize", comment: ""), message: message, preferredStyle: .alert)
alertCtrl.addAction(UIAlertAction(title: NSLocalizedString("Go", comment: ""), style: .default, handler: { (_) in
if let url = URL(string: UIApplication.openSettingsURLString) {
UIViewController.rootViewController?.openDefaultBrowser(withURL: url)
}
}))
alertCtrl.addAction(UIAlertAction(title: Localization.cancel, style: .destructive, handler: nil))
UIViewController.rootViewController?.present(alertCtrl, animated: true, completion: nil)
}
}
//
// ImagePicker.swift
//
// Created by Marcelo Perretta on 18/05/2022.
//
// Inspiration: https://theswiftdev.com/picking-images-with-uiimagepickercontroller-in-swift-5/
import Foundation
import UIKit
import Photos
import PhotosUI
import SwiftUI
public protocol ImagePickerDelegate: AnyObject {
func didSelect(image: UIImage?)
}
open class ImagePicker: NSObject {
private let imagePickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
self.imagePickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.imagePickerController.delegate = self
self.imagePickerController.allowsEditing = true
self.imagePickerController.mediaTypes = ["public.image"]
}
private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(type) else {
return nil
}
return UIAlertAction(title: title, style: .default) { [unowned self] _ in
self.imagePickerController.sourceType = type
if type == .camera {
AuthImagePickerSettings.authCamera(message: NSLocalizedString("Go to settings to authorize camera", comment: ""), completion: {
DispatchQueue.main.async {
self.presentationController?.present(self.imagePickerController, animated: true)
}
})
} else {
AuthImagePickerSettings.authPhotoLibrary(message: NSLocalizedString("Go to settings to authorize photo library", comment: ""), completion: {
DispatchQueue.main.async {
if #available(iOS 14, *) {
self.showPHPickerController()
} else {
self.presentationController?.present(self.imagePickerController, animated: true)
}
}
})
}
}
}
public func present(from sourceView: UIView) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let action = self.action(for: .camera, title: "📸 Take photo") {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: "🎞 Photo library") {
alertController.addAction(action)
}
alertController.addAction(UIAlertAction(title: Localization.cancel, style: .destructive, handler: nil))
if UIDevice.current.userInterfaceIdiom == .pad {
alertController.popoverPresentationController?.sourceView = sourceView
alertController.popoverPresentationController?.sourceRect = sourceView.bounds
alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
}
self.presentationController?.present(alertController, animated: true)
}
private func pickerController(_ controller: UIViewController, didSelect image: UIImage?) {
DispatchQueue.main.async {
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(image: image)
}
}
}
extension ImagePicker: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(picker, didSelect: nil)
}
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else {
return self.pickerController(picker, didSelect: nil)
}
self.pickerController(picker, didSelect: image)
}
}
@available(iOS 14, *)
extension ImagePicker: PHPickerViewControllerDelegate {
func showPHPickerController(limit: Int = 1) {
// new in iOS 14, we can get the asset _later_ so we don't need access up front
do {
// if you create the configuration with no photo library, you will not get asset identifiers
var config = PHPickerConfiguration()
// try it _with_ the library
config = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
config.selectionLimit = limit // default
// what you filter out will indeed not appear in the picker
config.filter = .any(of: [.images]) // default is all three appear, no filter
config.preferredAssetRepresentationMode = .current
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
AuthImagePickerSettings.authPhotoLibrary(message: NSLocalizedString("Go to settings to authorize photo library", comment: ""), completion: {
DispatchQueue.main.async {
self.presentationController?.present(picker, animated: true)
}
})
}
}
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
guard let firstResult = results.first else {
return self.pickerController(picker, didSelect: nil)
}
let prov = firstResult.itemProvider
guard prov.canLoadObject(ofClass: UIImage.self) else {
return self.pickerController(picker, didSelect: nil)
}
prov.loadObject(ofClass: UIImage.self) { [weak self] im, _ in
guard let image = im as? UIImage else {
self?.pickerController(picker, didSelect: nil)
return
}
self?.pickerController(picker, didSelect: image)
}
}
}
//
// ViewController.swift
//
// Created by Marcelo Perretta on 17/05/2022.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet var imageView: UIImageView!
var imagePicker: ImagePicker!
override func viewDidLoad() {
super.viewDidLoad()
self.imagePicker = ImagePicker(presentationController: self, delegate: self)
}
@IBAction func showImagePicker(_ sender: UIButton) {
self.imagePicker.present(from: sender)
}
}
extension ViewController: ImagePickerDelegate {
func didSelect(image: UIImage?) {
guard let image = image else {
return
}
self.avatar.imageView.image = image
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment