Last active
May 19, 2022 12:48
-
-
Save bubudrc/dac5915cefc9d9b36083ed715de14dd0 to your computer and use it in GitHub Desktop.
ImagePicker. Simple solution to use camera/photo library to pick an image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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