0

I am trying to create protocol where I can open UIimagePickerController with camera or Media Library according to user's choice.

here is some code:

import UIKit
protocol PFImagePickerProtocol: UIImagePickerControllerDelegate,UINavigationControllerDelegate where Self: UIViewController {
    func didSelectImage(image: UIImage?, error: Bool)
    func didCancelledImageSelection()
}
extension PFImagePickerProtocol {
    func openImageSelector(withCorp cropEnabled:Bool)  {
        let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet)

        let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
            self.openImagePicker(withCorp: cropEnabled, sourceType: .camera)
        }
        let library = UIAlertAction(title: "Photo Library", style: .default) { (action) in
            self.openImagePicker(withCorp: cropEnabled, sourceType: .photoLibrary)
        }
        alertController.addAction(camera)
        alertController.addAction(library)

        self.present(alertController, animated: true, completion: nil)
    }

    private func openImagePicker(withCorp cropEnabled:Bool, sourceType: UIImagePickerController.SourceType)  {
        let pickerVc = UIImagePickerController()
        pickerVc.allowsEditing = cropEnabled
        pickerVc.sourceType = sourceType
        pickerVc.delegate = self //is this the problem?
        self.present(pickerVc, animated: true, completion: nil)
    }
}

extension PFImagePickerProtocol{
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        self.dismiss(animated: true, completion: nil)
        didCancelledImageSelection()
    }
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            didSelectImage(image: image, error: false)
            return
        } else {
            didSelectImage(image: nil, error: true)
        }
        self.dismiss(animated: true, completion: nil)
    }
}

as I run the code. function 'didFinishPickingMediaWithInfo' is not called.

I found this answer useful. but if there anything that can solved this problem. kindly share it here.

feel free to comment on code.

nitish005
  • 106
  • 11

2 Answers2

0

Please write your code as below:

protocol PFImagePickerProtocol {
    func didSelectImage(image: UIImage?, error: Bool)
    func didCancelledImageSelection()
}

And write your extensions as below that contains the delegate methods:

extension YourViewController {
    func openImageSelector(withCorp cropEnabled:Bool)  {
        let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet)

        let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
            self.openImagePicker(withCorp: cropEnabled, sourceType: .camera)
        }
        let library = UIAlertAction(title: "Photo Library", style: .default) { (action) in
            self.openImagePicker(withCorp: cropEnabled, sourceType: .photoLibrary)
        }
        alertController.addAction(camera)
        alertController.addAction(library)

        self.present(alertController, animated: true, completion: nil)
    }

    private func openImagePicker(withCorp cropEnabled:Bool, sourceType: UIImagePickerController.SourceType)  {
        let pickerVc = UIImagePickerController()
        pickerVc.allowsEditing = cropEnabled
        pickerVc.sourceType = sourceType
        pickerVc.delegate = self //This will set your picker delegate to view controller class & the below extension conforms the delegates.
        self.present(pickerVc, animated: true, completion: nil)
    }
}

extension YourViewController: UIImagePickerControllerDelegate,UINavigationControllerDelegate{
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        self.dismiss(animated: true, completion: nil)
        didCancelledImageSelection()
    }
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            didSelectImage(image: image, error: false)
            return
        } else {
            didSelectImage(image: nil, error: true)
        }
        self.dismiss(animated: true, completion: nil)
    }
}

I think then you can write all of the above code in one single view controller like AbstractViewController which is a sub class of UIViewController, and all of your other view controllers that have this functionality have their super class as AbstractViewController.

class AbstractViewController: UIViewController {
    // Do above code here
}

class OtherViewController: AbstractViewController {
    // Classes that needs to implement the image picker functionality
}
Anshul Bhatheja
  • 673
  • 3
  • 21
  • we can't have inheritance clause while writing extension for protocol. – nitish005 Feb 19 '19 at 10:52
  • No, we cannot inherit while writing the protocols, we can only inherit while writing UIViewControllers or their extensions. – Anshul Bhatheja Feb 19 '19 at 11:08
  • Also only inherit those delegate or datasource that the given extension may confirm. – Anshul Bhatheja Feb 19 '19 at 11:08
  • Yes. I did try this code. gives an error "Extension of protocol 'PFImagePickerProtocol' cannot have an inheritance clause" – nitish005 Feb 20 '19 at 05:02
  • Then why are you extending the protocols, Just extend the view controller to implement the delegate & datasources in extensions. – Anshul Bhatheja Feb 20 '19 at 07:08
  • I have updated my code, Please check now just pass your view controller name to the extension. – Anshul Bhatheja Feb 20 '19 at 07:30
  • I wanted to avoid writing redundant code given in `openImageSelector ` function above. what you have suggested is right. But here I need to write `openImageSelector ` function in every viewcontroller where I want to pick an image. I was trying to solve this by creating protocol. – nitish005 Feb 20 '19 at 09:14
  • I did thout about writing the same way. but again it will increase some unnecessary code in my baseViewController. So, I did it by creating manager class which will do everything for view controller. I dropped the idea to create new protocol and extended my viewController(not the baseViewController) wherever required. – nitish005 Feb 21 '19 at 05:00
  • I have posted an answer. you can check – nitish005 Feb 21 '19 at 05:10
  • Ok, Thats great. Happy to hear that your problem is solved. – Anshul Bhatheja Feb 21 '19 at 06:04
0

class PFImagePickerManager

 typealias PFImagePickerTarget = UIImagePickerControllerDelegate & UINavigationControllerDelegate 
    class PFImagePickerManager {
        static var shared: PFImagePickerManager = PFImagePickerManager()
        var target: PFImagePickerTarget!
        private init() {}

         func openImageSelector(target: PFImagePickerTarget, shouldCrop: Bool) {
            self.target = target
            let alertController = UIAlertController(title: PFConstants.PFImagePicker.actionSheetTitle, message: kEmptyStr, preferredStyle: .actionSheet)
            let camera = UIAlertAction(title: PFConstants.PFImagePicker.camera, style: .default) { (action) in
                self.openImagePicker(withCorp: shouldCrop, sourceType: .camera)
            }
            let library = UIAlertAction(title: PFConstants.PFImagePicker.photoLibrary, style: .default) { (action) in
                self.openImagePicker(withCorp: shouldCrop, sourceType: .photoLibrary)
            }
            let cancel = UIAlertAction(title: PFConstants.PFImagePicker.cancel, style: .cancel) { (action) in

            }
            alertController.addAction(camera)
            alertController.addAction(library)
            alertController.addAction(cancel)
            if let vc = target as? PFBaseViewController {
                vc.present(alertController, animated: true, completion: nil)
            }
        }

        private func openImagePicker(withCorp cropEnabled:Bool, sourceType: UIImagePickerController.SourceType)  {

            let pickerVc = UIImagePickerController()
            pickerVc.allowsEditing = cropEnabled
            pickerVc.sourceType = sourceType
            pickerVc.delegate = target
            if sourceType == .photoLibrary {
                pickerVc.navigationBar.tintColor = UIColor.appThemePrimaryColor()
            }
            if let vc = target as? PFBaseViewController {
                vc.present(pickerVc, animated: true, completion: nil)
            }

        }

    }

you need to extend PFImagePickerTarget

extension YourViewController: PFImagePickerTarget {
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
            PFViewUtility.dispatchOnMainThread {
                self.VMCreateGR.changeDatafor(.image, data: image)
                self.tblInputs.reloadData()
            }
        } else {
            self.view.makeToast("Error while selecting image. Please try again.")
        }
        picker.dismiss(animated: true, completion: nil)
    }
}

and to initiate image picker in ViewController

    class AnyViewController: UIViewController {
        // In some method like viewDidLoad call the below line
        PFImagePickerManager.shared.openImageSelector(target: self, shouldCrop: true)
    }
Anshul Bhatheja
  • 673
  • 3
  • 21
nitish005
  • 106
  • 11