PickerController class, where I create the UIImagePickerController:
public protocol ImagePickerDelegate: class {
func didSelect(image: UIImage?)
}
open class ImagePicker: NSObject {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.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.pickerController.sourceType = type
self.presentationController?.present(self.pickerController, 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: "Сделать фото") {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: "Выбрать из галереи") {
alertController.addAction(action)
}
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, 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: UIImagePickerController, didSelect image: UIImage?) {
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(image: image)
}
}
extension ImagePicker: UIImagePickerControllerDelegate {
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)
}
}
extension ImagePicker: UINavigationControllerDelegate {
}
I guess, in didFinishPickingMediaWithInfo i should do something, but don't understand what exactly
class, where i work with pickerController:
class AddPlaneImageController: AddPlaneImageView {
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)
}
#IBAction func saveButton(_ sender: Any) {
RealmModel.shared.addPlaneImage(planeImage: "", aircompany: "") // here I'm supposed to save this image as String to Realm
}
}
extension AddPlaneImageController: ImagePickerDelegate {
func didSelect(image: UIImage?) {
self.planeImage.image = image
}
}
So, basically I don't understand, how I can turn UIImage() into String() and save it as String() too. I marked in the code, where I am supposed to do it
Related
This is my imagepicker class
import UIKit
public protocol ImagePickerDelegate: AnyObject {
func didSelect(image: UIImage?)
}
open class ImagePicker: NSObject {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.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.pickerController.sourceType = type
self.presentationController?.present(self.pickerController, animated:
true)
}
}
func openCamera() {
self.pickerController.sourceType = .camera
self.presentationController?.present(self.pickerController, animated: true)
}
func openPhotoLibrary() {
self.pickerController.sourceType = .photoLibrary
self.presentationController?.present(self.pickerController, 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: Localization.set(key: .take_photo)) {
alertController.addAction(action)
}
if let action = self.action(for: .savedPhotosAlbum, title: Localization.set(key: .camera_roll)) {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: Localization.set(key: .photo_library)) {
alertController.addAction(action)
}
alertController.addAction(UIAlertAction(title: Localization.set(key: .cancel), style: .cancel, 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: UIImagePickerController, didSelect image: UIImage?) {
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(image: image)
} }
extension ImagePicker: UIImagePickerControllerDelegate {
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)
} }
extension ImagePicker: UINavigationControllerDelegate {
}
This is how I used this class
self.imagePicker = ImagePicker(presentationController: self, delegate: self)
//MARK:- ImagePickerDelegate
extension AddEditGroupViewController: ImagePickerDelegate {
func didSelect(image: UIImage?) {
if let wnwrappedImage = image , let imageData = wnwrappedImage.jpegData(compressionQuality: 0){
self.changePhotoView.profileImageView.image = wnwrappedImage
imageParams = ["group_photo" : imageData]
}
}
}
It's working fine for photo gallery and saved photo album but When I try to open camera, it's giving me error as follows
**[Camera] Failed to read exposureBiasesByMode dictionary: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: data is NULL" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: data is NULL}**
Any help would be appreciated
I have an imagePicker, and when I initialize it, it takes pretty long time, it gets the screen to lag, for example, I have a screen where a user can write information and chose a picture, when I click on a button to move to that screen, it lags a bit before actually moving to that screen.
I ran the Time Profiler, and the problem with the screen seems to be the initialization of the imagePicker.
This is the imagePicker class:
import UIKit
public protocol ImagePickerDelegate: class {
func didSelect(image: UIImage?)
}
class ImagePicker: NSObject {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
init(presentationController: UIViewController, delegate: ImagePickerDelegate){
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.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, handler: { [unowned self] _ in
self.pickerController.sourceType = type
self.presentationController?.present(self.pickerController, animated: true)
})
}
func present(from sourceView: UIView){
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let action = self.action(for: .camera, title: ImagePickerStrings.takePicture){
alertController.addAction(action)
}
if let action = self.action(for: .savedPhotosAlbum, title: ImagePickerStrings.cameraRoll) {
alertController.addAction(action)
}
alertController.addAction(UIAlertAction(title: GeneralStrings.cancel, style: .cancel, handler: nil))
self.presentationController?.present(alertController, animated: true)
}
private func pickerController(_ controller: UIImagePickerController, didSelect image: UIImage?){
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(image: image)
}
}
extension ImagePicker: UIImagePickerControllerDelegate{
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)
}
}
And this is how I initialize it:
I have this variable inside the class:
var imagePicker: ImagePicker!
This is in the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
imagePicker = ImagePicker(presentationController: self, delegate: self)
}
It's usually very slow on simulator and overall in debug mode.
See UIImagePickerController really slow when calling alloc init
I am working with Coordinators.
My ViewController is not deallocating even though I set weak delegates.
Coordinator:
class JournalDetailCoordinator: Coordinator {
var dependencys: AppDependency
var navigationController: UINavigationController
var collectionViewController: CollectionViewWithMenuController!
var imagePickerManager: ImagePickerManager!
init(dependencys: AppDependency, navigationController: UINavigationController) {
self.dependencys = dependencys
self.navigationController = navigationController
}
func start() {
loadCollectionViewController()
}
deinit {
print("JournalDetailCoordinator deinitialisiert")
}
func loadCollectionViewController() {
var journalDetailViewControllerContainer = [JournalDetailViewController]()
for journal in dependencys.journals {
let vc: JournalDetailViewController = dependencys.getJournalDetailDependency().createVC()
vc.entryJournal = journal
vc.delegateLoadImagePickerManager = self
journalDetailViewControllerContainer.append(vc)
}
collectionViewController = dependencys.getCollectionViewWithMenuDependency().createVC()
collectionViewController.managedViewControllers = journalDetailViewControllerContainer
navigationController.pushViewController(collectionViewController, animated: true)
}
}
extension JournalDetailCoordinator: LoadImagePickerManager {
func loadImagePickerManager<T>(vc: T) where T : UIViewController & ImageGetterDelegate {
imagePickerManager = ImagePickerManager()
imagePickerManager.delegate = vc
imagePickerManager.pickImage(viewController: collectionViewController)
}
}
ViewController:
class JournalDetailViewController: UIViewController {
lazy var mainView: JournalDetailViewP = {
let view = JournalDetailViewP()
return view
}()
typealias myType = SetJournal & HasImagePickerManager
// dependency
var dep: myType!
var entryJournal: Journaling!
var tableViewDataSource: JournalDetailTVDataSource?
var collectionViewInteraction: AddImageCollectionViewInteraction?
weak var delegateLoadImagePickerManager: LoadImagePickerManager?
override func viewDidLoad() {
super.viewDidLoad()
title = "Detail Journal"
// only for testing without coordinator connection
// if entryJournal == nil {
// entryJournal = NewJournal()
// }
// dep = AppDependency()
setMainView()
loadTableView()
loadCollectionView()
}
override func viewDidDisappear(_ animated: Bool) {
print("view did disappear Journal Detail")
}
deinit {
dep.setJournal(newJournal: entryJournal)
print("JournalDetailViewController deinitialisiert")
}
#objc func getImage() {
delegateLoadImagePickerManager?.loadImagePickerManager(vc: self)
// dep.imagePickerManager.delegate = self
// dep.imagePickerManager.pickImage(viewController: self)
}
func saveEntry() {
}
}
extension JournalDetailViewController: Storyboarded {}
extension JournalDetailViewController: DependencyInjectionVC {}
extension JournalDetailViewController: SetMainView {}
extension JournalDetailViewController: ImageGetterDelegate {
func returnImage(image: UIImage) {
if entryJournal.image[0] == nil {
entryJournal.image[0] = image
} else {
entryJournal.image.append(image)
}
loadCollectionView()
}
}
extension JournalDetailViewController: AddImageCollectionViewInteractionDelegate {
func deleteImage(index: Int) {
}
func addImage() {
getImage()
}
}
They are deallocation if I do not execute the getImage() function, so I think that is the reason of the retention circle.
Thats the ImagePickerManager:
protocol ImageGetterDelegate: class {
func returnImage(image: UIImage)
}
class ImagePickerManager: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var imagePicker = UIImagePickerController()
weak var delegate: ImageGetterDelegate?
override init() {
super.init()
print("ImagePickerManager initialisiert")
}
deinit {
print("imagePickerManager deinitialisiert")
}
/// use to pick the Image, make sure to use the root ViewController to pass in to
func pickImage<T:UIViewController>(viewController: T) {
let alertList = UIAlertController(title: NSLocalizedString("Load Picture", comment: "Picture alert Alertcontroller"), message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) {
UIAlertAction in self.openCamera(viewController: viewController)
alertList.dismiss(animated: true, completion: nil)
}
let galleryAction = UIAlertAction(title: "Gallery", style: .default) {
UIAlertAction in self.openGallery(viewController: viewController)
alertList.dismiss(animated: true, completion: nil)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
alertList.dismiss(animated: true, completion: nil)
}
alertList.addAction(cameraAction)
alertList.addAction(galleryAction)
alertList.addAction(cancelAction)
viewController.present(alertList, animated: true, completion: nil)
}
private func openCamera<T:UIViewController>(viewController: T) {
if(UIImagePickerController .isSourceTypeAvailable(.camera)) {
imagePicker.sourceType = .camera
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
} else {
let warningAlert = UIAlertController(title: "Warning", message: "You do not have a camera", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Okay", style: .cancel) {
UIAlertAction in
warningAlert.dismiss(animated: true, completion: nil)
}
warningAlert.addAction(cancelAction)
viewController.present(warningAlert, animated: true, completion: nil)
}
}
private func openGallery<T:UIViewController>(viewController: T) {
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
}
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[.originalImage] as? UIImage else {
print("Expected a dictionary containing an image, but was provided the following: \(info)")
return
}
delegate?.returnImage(image: image)
}
}
ImagePickerManager is not allocating after the Coordinator is deallocated. So I think the Retention circle is because I pass the ViewVontroller back through to the Coordinator in LoadImagePickerManager and then set the vc to the Coordinator? Does anybody have an idea how to solve that problem or what to do?
Edit:
LoadImagePickerManager:
protocol LoadImagePickerManager: class {
func loadImagePickerManager<T: UIViewController & ImageGetterDelegate>(vc: T)
}
I think the memory leak happens here when passing the collectionViewController:
imagePickerManager.pickImage(viewController: collectionViewController)
Because I did some tests if I do not execute this part then everything is deallocating fine.
Updated ImagePickerManager class:
class ImagePickerManager: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var imagePicker = UIImagePickerController()
weak var delegate: ImageGetterDelegate?
var viewController: UIViewController!
override init() {
super.init()
print("ImagePickerManager initialisiert")
}
deinit {
print("imagePickerManager deinitialisiert")
}
/// use to pick the Image, make sure to use the root ViewController to pass in to
func pickImage<T:UIViewController>(viewController: T) {
self.viewController = viewController
let alertList = UIAlertController(title: NSLocalizedString("Load Picture", comment: "Picture alert Alertcontroller"), message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) {
UIAlertAction in self.openCamera()
alertList.dismiss(animated: true, completion: nil)
}
let galleryAction = UIAlertAction(title: "Gallery", style: .default) {
UIAlertAction in self.openGallery()
alertList.dismiss(animated: true, completion: nil)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
alertList.dismiss(animated: true, completion: nil)
}
alertList.addAction(cameraAction)
alertList.addAction(galleryAction)
alertList.addAction(cancelAction)
viewController.present(alertList, animated: true, completion: nil)
}
private func openCamera() {
if(UIImagePickerController .isSourceTypeAvailable(.camera)) {
imagePicker.sourceType = .camera
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
} else {
let warningAlert = UIAlertController(title: "Warning", message: "You do not have a camera", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Okay", style: .cancel) {
UIAlertAction in
warningAlert.dismiss(animated: true, completion: nil)
}
warningAlert.addAction(cancelAction)
viewController.present(warningAlert, animated: true, completion: nil)
}
}
private func openGallery() {
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
}
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[.originalImage] as? UIImage else {
print("Expected a dictionary containing an image, but was provided the following: \(info)")
return
}
viewController = nil
delegate?.returnImage(image: image)
}
}
I added a viewController variable to the class, and set it through the pickImage() and then when the image is selected I set the variable to nil. Then the UIViewController gets deallocated, but still the class ImagePickerManager stays alive and does not get allocated.
Since you are using weak delegate so, in no way it is going to create a retain cycle.
I think your viewController is not deallocating because your viewController is still in the stack of your navigation.
Try to remove all the viewControllers from the navigation stack and then your deallocate block will work as usual.
Try the following code depending upon your requirement(present/push) when you are coming back to your homeViewController:
self.navigationController?.popToRootViewController(animated: true)
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
Edit:
Make sure your protocol is a class type then only weak reference will work.
protocol LoadImagePickerManager: class {
}
In your PickerManager try to dismiss using following code, it will redirect you to rootview controller, but you can again push or present to the required viewcontroller:
self.view.window?.rootViewController?.dismiss(animated: false, completion: nil)
I'm implementing a photopicker in my project and it works or not, with the same code, depending the way I implement it. In my first approach I was using a custom UIAlertAction class and doing all the stuff in there, to have my main controller lighter, but the picker delegate was never called, instead it prints an error message in the console ([discovery] errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo={NSLocalizedDescription=query cancelled}) and I would like to discuss if the way I were implementing the picker was right or wrong and why? Or if it's a bug from apple. I've been googling for a while and checking all the related questions in stack overflow, and anything has worked for me except to put the picker code in my main controller.
Here is the code of my main controller, when it was not calling the delegate:
First approach that give me error and don't call delegates
import UIKit
import MobileCoreServices
import Photos
class ViewController: UIViewController {
#IBOutlet weak var lblMain: UILabel!
#IBOutlet weak var buttonsBottom: NSLayoutConstraint!
#IBOutlet weak var imgFromUser: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
hideButtons()
}
override func viewDidAppear(_ animated: Bool) {
showButtons()
checkPermission()
}
func checkPermission() {
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized: print("Access is granted by user")
case .notDetermined: PHPhotoLibrary.requestAuthorization({
(newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized { print("success") }
})
case .restricted: print("User do not have access to photo album.")
case .denied: print("User has denied the permission.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func hideButtons()
{
buttonsBottom.constant += self.view.frame.size.height * 0.15
}
func showButtons(){
UIView.animate(withDuration: 0.5) {
}
UIView.animate(withDuration: 0.5, animations: {
self.buttonsBottom.constant = 0
self.view.layoutIfNeeded()
}) { (completed) in
self.lblMain.text = firstController.lblMain
}
}
#IBAction func addImagePressed(_ sender: Any) {
let alertViewController : AlertAction = AlertAction.init(controller: self, type: .photoGallery)
self.present((alertViewController.setType(alert: .photoGallery)), animated: true, completion: nil)
}
}
And this is my custom AlertAction class, it implements the picker & navigation delegate, creates the UIImagePickerController, sets the delegate to it, creates the alert action and set it to present the main view controller, adds it to a UIAlertController and gets returned to the main view controller which present it:
import UIKit
import Photos
class AlertAction: UIAlertAction {
var destinationController : ViewController?
var imagePicker = UIImagePickerController()
convenience init(controller : ViewController, type : type) {
self.init()
destinationController = controller
//Init picker
imagePicker.delegate = self
imagePicker.sourceType = type == .photoGallery ? UIImagePickerControllerSourceType.photoLibrary : UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = false
}
enum type {
case camera
case photoGallery
}
var alertType : type = .camera
func setType(alert : type) -> UIAlertController {
alertType = alert
return alertType == .camera ? newAlert() : newAlert()
}
func newAlert() -> UIAlertController{
//set alert text
let alertText = alertType == .camera ? AlertText.typeCamera : AlertText.typeGalery
let myAlert = UIAlertController(title: AlertText.title, message: "", preferredStyle: .actionSheet)
if alertType == .camera {
myAlert.addAction(getCameraAction(alertText: alertText))
return myAlert
}
else{
myAlert.addAction(getGalleryAction(alertText: alertText))
return myAlert
}
}
func getCameraAction(alertText : String) -> UIAlertAction{
let cameraAction = UIAlertAction(title : alertText, style : .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
self.destinationController!.present(self.imagePicker, animated: true, completion: nil)
}
}
return cameraAction
}
func getGalleryAction(alertText : String) -> UIAlertAction{
let photoLibraryAction = UIAlertAction(title: alertText, style: .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
self.destinationController!.present(self.imagePicker, animated: true, completion: nil)
}
}
return photoLibraryAction
}
}
extension AlertAction : UIImagePickerControllerDelegate {
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if mediaType.isEqual(to: kCIAttributeTypeImage as String){
destinationController!.imgFromUser.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
destinationController!.dismiss(animated: true, completion: nil)
}
#objc func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
destinationController!.dismiss(animated: true, completion: nil)
}
}
extension AlertAction : UINavigationControllerDelegate {
}
After reading a while, I've been trying all the posible solutions in this custom class but nothing worked.
Then I tried to implement a picker creation method in my main view controller and it worked.
So my question is very simple, why the delegate methods get called only if I do all the coding stuff in the main view controller but don't work in a custom class?
Here is the code that I'm currently using and works:
Second approach that works, all the code in the same ViewController
import UIKit
import MobileCoreServices
import Photos
class ViewController: UIViewController {
#IBOutlet weak var lblMain: UILabel!
#IBOutlet weak var buttonsBottom: NSLayoutConstraint!
#IBOutlet weak var imgFromUser: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
hideButtons()
}
override func viewDidAppear(_ animated: Bool) {
showButtons()
checkPermission()
}
func checkPermission() {
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized: print("Access is granted by user")
case .notDetermined: PHPhotoLibrary.requestAuthorization({
(newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized { print("success") }
})
case .restricted: print("User do not have access to photo album.")
case .denied: print("User has denied the permission.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func hideButtons()
{
buttonsBottom.constant += self.view.frame.size.height * 0.15
}
func showButtons(){
UIView.animate(withDuration: 0.5) {
}
UIView.animate(withDuration: 0.5, animations: {
self.buttonsBottom.constant = 0
self.view.layoutIfNeeded()
}) { (completed) in
self.lblMain.text = firstController.lblMain
}
}
#IBAction func addImagePressed(_ sender: Any) {
self.present(addPicker(), animated: true, completion: nil)
}
//MARK: Picker methods
func addPicker()->UIAlertController{
let alertText = AlertText.typeGalery
let myAlert = UIAlertController(title: AlertText.title, message: "", preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title : alertText, style : .default) { (action) in
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
}
myAlert.addAction(cameraAction)
return myAlert
}
}
extension ViewController : UIImagePickerControllerDelegate {
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage{
self.imgFromUser.image = image
}
if mediaType.isEqual(to: kCIAttributeTypeImage as String){
self.imgFromUser.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
self.dismiss(animated: true, completion: nil)
}
#objc func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
}
extension ViewController : UINavigationControllerDelegate {
}
Update: the error [discovery] errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo={NSLocalizedDescription=query cancelled} still appears in my second implementation, but only when I remove #objc before the picker delegate method and environement varibable OS_ACTIVITY_MODE = disable is not set, but the code still works anda the delegates are called correctly (in the second implementation) so basically this error is not related with the code functionality and don't describe anything useful
I am trying hard to understand how this works, but it's pretty hard for me. =)
I have 1 view, there is one button and one small ImageView area for preview.
The button triggers imagepickercontroller, and the UIView will display picked image.
There is no error but the image doesn't show in the UIImageView area.
var imagePicker = UIImagePickerController()
#IBOutlet var imagePreview : UIImageView
#IBAction func AddImageButton(sender : AnyObject) {
imagePicker.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
imagePicker.delegate = self
self.presentModalViewController(imagePicker, animated: true)
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingMediaWithInfo info:NSDictionary!) {
var tempImage:UIImage = info[UIImagePickerControllerOriginalImage] as UIImage
imagePreview.image = tempImage
self.dismissModalViewControllerAnimated(true)
}
func imagePickerControllerDidCancel(picker: UIImagePickerController!) {
self.dismissModalViewControllerAnimated(true)
}
You're grabbing a UIImage named UIImagePickerControllerOriginalImage and there exists no such image. You're meant to grab the UIImage with the key UIImagePickerControllerOriginalImage from the editingInfo dictionary:
let tempImage = editingInfo[UIImagePickerControllerOriginalImage] as! UIImage
Details
Xcode 11.4.1 (11E503a), Swift 5.2
More
How to build own photo picker
Solution
import UIKit
import AVFoundation
import Photos
protocol ImagePickerDelegate: class {
func imagePicker(_ imagePicker: ImagePicker, grantedAccess: Bool,
to sourceType: UIImagePickerController.SourceType)
func imagePicker(_ imagePicker: ImagePicker, didSelect image: UIImage)
func cancelButtonDidClick(on imageView: ImagePicker)
}
class ImagePicker: NSObject {
private weak var controller: UIImagePickerController?
weak var delegate: ImagePickerDelegate? = nil
func dismiss() { controller?.dismiss(animated: true, completion: nil) }
func present(parent viewController: UIViewController, sourceType: UIImagePickerController.SourceType) {
let controller = UIImagePickerController()
controller.delegate = self
controller.sourceType = sourceType
self.controller = controller
DispatchQueue.main.async {
viewController.present(controller, animated: true, completion: nil)
}
}
}
// MARK: Get access to camera or photo library
extension ImagePicker {
private func showAlert(targetName: String, completion: ((Bool) -> Void)?) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
let alertVC = UIAlertController(title: "Access to the \(targetName)",
message: "Please provide access to your \(targetName)",
preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "Settings", style: .default, handler: { action in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(settingsUrl) else { completion?(false); return }
UIApplication.shared.open(settingsUrl, options: [:]) { [weak self] _ in
self?.showAlert(targetName: targetName, completion: completion)
}
}))
alertVC.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in completion?(false) }))
UIApplication.shared.windows.filter { $0.isKeyWindow }.first?
.rootViewController?.present(alertVC, animated: true, completion: nil)
}
}
func cameraAsscessRequest() {
if delegate == nil { return }
let source = UIImagePickerController.SourceType.camera
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
delegate?.imagePicker(self, grantedAccess: true, to: source)
} else {
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
guard let self = self else { return }
if granted {
self.delegate?.imagePicker(self, grantedAccess: granted, to: source)
} else {
self.showAlert(targetName: "camera") { self.delegate?.imagePicker(self, grantedAccess: $0, to: source) }
}
}
}
}
func photoGalleryAsscessRequest() {
PHPhotoLibrary.requestAuthorization { [weak self] result in
guard let self = self else { return }
let source = UIImagePickerController.SourceType.photoLibrary
if result == .authorized {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.delegate?.imagePicker(self, grantedAccess: result == .authorized, to: source)
}
} else {
self.showAlert(targetName: "photo gallery") { self.delegate?.imagePicker(self, grantedAccess: $0, to: source) }
}
}
}
}
// MARK: UINavigationControllerDelegate
extension ImagePicker: UINavigationControllerDelegate { }
// MARK: UIImagePickerControllerDelegate
extension ImagePicker: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.editedImage] as? UIImage {
delegate?.imagePicker(self, didSelect: image)
return
}
if let image = info[.originalImage] as? UIImage {
delegate?.imagePicker(self, didSelect: image)
} else {
print("Other source")
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
delegate?.cancelButtonDidClick(on: self)
}
}
Full Usage
Info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>bla-bla-bla</string>
<key>NSCameraUsageDescription</key>
<string>bla-bla-bla</string>
ViewController (do not forget to paste solution code)
import UIKit
class ViewController: UIViewController {
private weak var imageView: UIImageView!
private lazy var imagePicker: ImagePicker = {
let imagePicker = ImagePicker()
imagePicker.delegate = self
return imagePicker
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "camera", style: .plain, target: self,
action: #selector(cameraButtonTapped))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "photo", style: .plain, target: self,
action: #selector(photoButtonTapped))
let imageView = UIImageView(frame: CGRect(x: 40, y: 80, width: 200, height: 200))
imageView.backgroundColor = .lightGray
view.addSubview(imageView)
self.imageView = imageView
}
#objc func photoButtonTapped(_ sender: UIButton) { imagePicker.photoGalleryAsscessRequest() }
#objc func cameraButtonTapped(_ sender: UIButton) { imagePicker.cameraAsscessRequest() }
}
// MARK: ImagePickerDelegate
extension ViewController: ImagePickerDelegate {
func imagePicker(_ imagePicker: ImagePicker, didSelect image: UIImage) {
imageView.image = image
imagePicker.dismiss()
}
func cancelButtonDidClick(on imageView: ImagePicker) { imagePicker.dismiss() }
func imagePicker(_ imagePicker: ImagePicker, grantedAccess: Bool,
to sourceType: UIImagePickerController.SourceType) {
guard grantedAccess else { return }
imagePicker.present(parent: self, sourceType: sourceType)
}
}
Images
import MobileCoreServices
class SecondViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet var img:UIImageView!=nil
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
#IBAction func buttonTapped(AnyObject)
{
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary){
println("Button capture")
var imag = UIImagePickerController()
imag.delegate = self
imag.sourceType = UIImagePickerControllerSourceType.PhotoLibrary;
//imag.mediaTypes = [kUTTypeImage];
imag.allowsEditing = false
self.presentViewController(imag, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!) {
let selectedImage : UIImage = image
//var tempImage:UIImage = editingInfo[UIImagePickerControllerOriginalImage] as UIImage
img.image=selectedImage
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Since swift 4.2 and xcode 10 the func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) change a bit.
at the function header the infoKey-Section changes from
didFinishPickingMediaWithInfo info: [String : Any] to
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]
To get the originalImage you now have to call:
let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage or
let image = info[.originalImage] as? UIImage
find other infoKeys here (editedImage, imageURL and more...)
In Swift 5.2, iOS 14.2
class YourViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBAction func btnTapped(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = false //If you want edit option set "true"
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let tempImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
imgRoom.image = tempImage
self.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
}
Try this:
func imagePickerController(picker: UIImagePickerController!, didFinishPickingMediaWithInfo info:NSDictionary!) {
let tempImage = info[UIImagePickerControllerOriginalImage] as! UIImage
imagePreview.image = tempImage
or you can also use ? in place of !.