I have a fairly simply NSObject class that is what I am calling a MediaPicker. This class asks the user what type of media they want to pick and subsequently presents an UIImagePickerController or a UIDocumentPickerViewController based on their selection.
The problem I'm facing is that the UIImagePickerControllerDelegate and the UIDocumentPickerViewControllerDelegate methods are not being called.
My class is below:
import Foundation
import UIKit
import MobileCoreServices
public protocol MediaPickerDelegate: NSObjectProtocol {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
}
class MediaPicker: NSObject {
//Data
var viewController: UIViewController? = nil
var imagePicker: UIImagePickerController = UIImagePickerController()
let documentPicker = UIDocumentPickerViewController(documentTypes: [(kUTTypeImage as String), (kUTTypeMovie as String)], in: .import)
//Delegate
weak var delegate: MediaPickerDelegate? = nil
override init() {
super.init()
}
public func showMediaPicker(from presentingViewController: UIViewController) {
//Set Data
self.viewController = presentingViewController
//Create Alert
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
//Add Library Action
let libraryAction: UIAlertAction = UIAlertAction(title: "Photo & Video Library", style: .default) { (action) in
self.presentLibraryPicker()
}
libraryAction.setValue(UIImage(named: "gallery_picker_library"), forKey: "image")
libraryAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
alertController.addAction(libraryAction)
//Add Cloud Action
let cloudAction: UIAlertAction = UIAlertAction(title: "Other Locations", style: .default) { (action) in
self.presentDocumentPicker()
}
cloudAction.setValue(UIImage(named: "gallery_picker_cloud"), forKey: "image")
cloudAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
alertController.addAction(cloudAction)
//Add Cancel Action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
//Show Alert
self.viewController?.present(alertController, animated: true, completion: nil)
}
private func presentLibraryPicker() {
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .savedPhotosAlbum) ?? []
imagePicker.allowsEditing = true
self.viewController?.present(imagePicker, animated: true, completion: nil)
}
private func presentDocumentPicker() {
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
self.viewController?.present(documentPicker, animated: true, completion: nil)
}
}
extension MediaPicker: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
delegate?.imagePickerController(picker, didFinishPickingMediaWithInfo: info)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
delegate?.imagePickerControllerDidCancel(picker)
}
}
extension MediaPicker: UIDocumentPickerDelegate {
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
delegate?.documentPickerWasCancelled(controller)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
delegate?.documentPicker(controller, didPickDocumentsAt: urls)
}
}
extension MediaPicker: UINavigationControllerDelegate {
//Redundant delegate implented as part of UIImagePickerControllerDelegate
}
The class calling this looks like this:
class Foo: UIViewController {
#objc func openPicker() {
let mediaPicker: MediaPicker = MediaPicker()
mediaPicker.delegate = self
mediaPicker.showMediaPicker(from: self)
}
}
I've tried making the protocol for MediaPickerDelegate both a class and NSObjectProtocol but with no luck. I also tried (thinking maybe an ARC issue from my obj-c days) making the two picker variables global and local in scope to the function but this didn't seem to change anything.
I'm also open to any feedback you have on the quality of the code that's been written/best practices that I may have missed.
Ok, so I've done some testing/tweaking and have realised that both of the document/image picker controllers are being removed from memory as soon as they're displayed. This is because they are local to the scope of the function that is calling them and the function has finished executing.
What I mean here is that in the function openPicker (from the question) the class that is calling it is doing some ARC magic and removing it as soon as it thinks the function is finished being called. Because the mediaPicker is local to openPicker and not global to class Foo it doesn't understand that it needs to keep it in memory.
The solution here is to have a global variable at a class level inside class Foo and access that inside of the function openPicker instead of initialising a new instance of MediaPicker as follows:
class Foo: UIViewController {
//Data
var mediaPicker: MediaPicker = MediaPicker()
#objc func openPicker() {
self.mediaPicker.delegate = self
self.mediaPicker.showMediaPicker(from: self)
}
}
Once I migrated to using this approach, the delegates are being called and the debugger clearly shows that the object is being retained in memory whilst the class/UIViewController is still interactively available to the user.
Have try to get permission for Photo Library(Privacy - Photo Library Usage Description) in Info.plist?
Related
I already worked on UIImagePickerController. This code was already works fine in Xcode 11.3. But when I run on Xcode 12 Image picker delegate is not calling in Xcode12.
/// Picked Image
struct PickedImage {
var image: UIImage?
var api: String?
}
/// Image picker
class ImagePicker: NSObject {
typealias ImagePickerHandler = ((_ selected: PickedImage) -> ())
private weak var presentationController: UIViewController?
let pickerController: UIImagePickerController = UIImagePickerController()
var apiKey: String?
private var handler: ImagePickerHandler? = nil
private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(type) else {
return nil
}
return UIAlertAction(title: title, style: .default) { (action) in
DispatchQueue.main.async {
self.pickerController.mediaTypes = ["public.image"]
self.pickerController.sourceType = type
self.pickerController.delegate = self
self.presentationController?.present(self.pickerController, animated: true, completion: {
})
}
}
}
/// Present source view
/// - Parameter sourceView: view
func present(presentationController: UIViewController, completed: ImagePickerHandler? = nil) {
self.handler = completed
self.presentationController = presentationController
// self.delegate = delegate
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: .savedPhotosAlbum, title: "Camera roll") {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: "Photo library") {
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(didSelect image: UIImage?, imageURL: URL?) {
pickerController.dismiss(animated: true, completion: nil)
// self.delegate?.imagePicker(picker: self, didSelected: image, apikey: apiKey)
handler?(PickedImage(image: image, api: apiKey))
}
}
/// ImagePicker controller delegate
extension ImagePicker: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(didSelect: nil, imageURL: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
self.pickerController(didSelect: info[.originalImage] as? UIImage, imageURL: info[.imageURL] as? URL)
}
}
When I check delegate is applied or not using breakpoint. like in console means
po imagepicker.delegate
there after image picker delegate was working fine. But when I remove breakpoint its delegate is not calling.
I don't know what is the reason. Why its not working. May I know how to fix this problem.
is there any reason to not put pickerController.delegate = self before self.presentationController?.present(pickerController, animated: true, completion: {})'?
if no, maybe you can put pickerController.delegate = self before that, and try again.
This is most likely because you're not retaining your picker controller in a variable. As soon as your function finishes, it gets deallocated.
For example I have something like this:
class MyClass: UIImagePickerControllerDelegate {
let imagePicker = UIImagePickerController()
}
func pickImageFromGallery() {
self.imagePicker.delegate = self
self.imagePicker.sourceType = UIImagePickerController.SourceType.photoLibrary
self.imagePicker.allowsEditing = false
self.present(self.imagePicker, animated: true, completion: nil)
}
... and the delegate methods as well
You code was always wrong; it's just lucky if it ever worked:
let pickerController: UIImagePickerController = UIImagePickerController()
pickerController.mediaTypes = ["public.image"]
pickerController.sourceType = "Photo library"
self.presentationController?.present(pickerController, animated: true, completion: {
pickerController.delegate = self
})
Change to:
let pickerController: UIImagePickerController = UIImagePickerController()
pickerController.mediaTypes = ["public.image"]
pickerController.sourceType = .photoLibrary
pickerController.delegate = self
self.present(pickerController, animated: true)
I think you are doing a silly mistake. just change a few lines of code and then you are good to go.
just follow my Steps
=> Here ProfileViewController is my UIViewController where I am going to pick Image from Gallery and Set image to UIImageView. You have to use your UIViewController where you want to Pick an Image.
ProfileViewController: UIViewController{
let pickerController = UIImagePickerController()
viewDidLoad(){
}
#IBAction func pickImageAction(sender: UIButton){
self.openImagePicker()
}
func openImagePicker()
{
pickerController.delegate = self
pickerController.allowsEditing = true
pickerController.mediaTypes = ["public.image", "public.movie"]
pickerController.sourceType = .photoLibrary // Pick image from PhotoLibrary
//pickerController.sourceType = .savedPhotosAlbum // Pick Saved Images
//pickerController.sourceType = .camera // Click Image using Camera
self.present(pickerController, animated: true)
} //End of setupImagePicker
} // End of ProfileViewController
// MARK:- Delegate method for UIImagePicker
extension ProfileViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
// image is your image which you picked from Image Gallery or using Camera.
// you can set this image directly to your UIImageView.
// self.yourImageView.image = image
// or you can compress this image, converting to JPG image type.
// compressionQuality will reduce your image quality and Image Size.
if let jpegData = image.jpegData(compressionQuality: 0.8) {
// you can use this compressed image.
}
self.dismiss(animated: true)
}// End of didFinishPickingMediaWithInfo
} // End of Extension
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
Using the code below it opens the camera but fails to call the picker delegate method. I'm getting no error messages.
import Foundation
import UIKit
import MobileCoreServices
class RecVidController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
RecVidController.startRecord(delegate: self, sourceType: .camera)
}
static func startRecord(delegate: UIViewController & UINavigationControllerDelegate & UIImagePickerControllerDelegate, sourceType: UIImagePickerController.SourceType) {
guard UIImagePickerController.isSourceTypeAvailable(sourceType) else { return }
let mediaUI = UIImagePickerController()
mediaUI.sourceType = sourceType
mediaUI.mediaTypes = [kUTTypeMovie as String]
mediaUI.allowsEditing = true
mediaUI.delegate = delegate
delegate.present(mediaUI, animated: true, completion: nil)
}
#objc func video(_ videoPath: String, didFinishSavingWithError error: Error?, contextInfo info: AnyObject) {
let title = (error == nil) ? "Success" : "Error"
let message = (error == nil) ? "Video was saved" : "Video failed to save"
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
}
// MARK: - UIImagePickerControllerDelegate
extension RecVidController: UIImagePickerControllerDelegate {
private func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
dismiss(animated: true, completion: nil)
guard let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
mediaType == (kUTTypeMovie as String),
let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL,
UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(url.path)
else { return }
// Handle a movie capture
UISaveVideoAtPathToSavedPhotosAlbum(url.path, self, #selector(video(_:didFinishSavingWithError:contextInfo:)), nil)
}
}
// MARK: - UINavigationControllerDelegate
extension RecVidController: UINavigationControllerDelegate {
}
The problem is this declaration:
private func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
You have declared this method private, so Objective-C has no way of knowing that it exists. Thus, it can't call into it.
Basically, Cocoa looks to see whether this method is implemented, discovers that it isn't (because you've hidden it), and gives up. There's no visible penalty, because this delegate method is optional, and when it is not implemented, Cocoa dismisses the picker for you when the user is finished with it.
So just delete private and you should be good to go. This exposes the delegate method to Objective-C, and so it will be called.
(You do not have to say #objc to expose it to Objective-C, as you would if this were your own function, because you've declared we adopt UIImagePickerControllerDelegate, which is an Objective-C protocol.)
I had a similar issue when delegate methods were not called by program.
The imagePicker.delegate = self should be NOT in viewDidLoad() method but in method which opens gallery. Like this:
func openGallery()
{
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
self.present(imagePicker, animated: true, completion: nil)
}
On the surface I thought that this had to be a delegate issue, but after asking for the delegate the right one was returned.
I created an ImagePicker class to handle all the UIImagePickerController stuff. Every thing works until the delegate methods need to be called. After I pick a photo, the imagePicker dismisses, but the didFinishPickingMediaWithInfo method never gets called. Please help! Thanks :)
func selectPhoto() {
imagePicker.delegate = self //Delegate gets set here
let photoAsk = UIAlertController.init( //Ask user if they want to take picture or choose one
title: "Edit Profile Picture",
message: nil,
preferredStyle: .alert)
let cameraAction = UIAlertAction.init(
title: "Take Photo",
style: .default) { (UIAlertAction) in
if (UIImagePickerController.isSourceTypeAvailable(.camera)) {
self.imagePicker.sourceType = .camera
UIApplication.topViewController()!.present(self.imagePicker, animated: true, completion:nil)
} else {
print("Cannot access camera in simulator.")
return
}
}
let photoLibraryAction = UIAlertAction.init(
title: "Photo Library",
style: .default) { (UIAlertAction) in
self.imagePicker.sourceType = .photoLibrary
UIApplication.topViewController()!.present(self.imagePicker, animated: true, completion:nil)
print("UIImagePickerDelegate: \(self.imagePicker.delegate.debugDescription)") // <--THIS PRINTS OUT "AppName.ImagePicker: 0x145d7bdf0>", and the class name is ImagePicker
}
let cancelAction = UIAlertAction.init(
title: "Cancel",
style: .cancel) { (UIAlertAction) in return }
photoAsk.addAction(cameraAction)
photoAsk.addAction(photoLibraryAction)
photoAsk.addAction(cancelAction)
imagePicker.mediaTypes = [kUTTypeImage as String]
UIApplication.topViewController()?.present(photoAsk, animated: true, completion: nil)
}
}
This never gets called:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("Image picked.") //NEVER PRINTS
}
I had to copy the method names straight from the delegate. For some reason the auto-complete has the method headers wrong.
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
//save image
//display image
}
self.dismiss(animated: true, completion: nil)
}
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
UPDATE SWIFT 5:
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
//save image
//display image
}
self.dismiss(animated: true, completion: nil)
}
Details
Xcode 9.2, Swift 4
Xcode 10.2.1 (10E1001), Swift 5
Solution
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
print("\(info)")
if let image = info[.originalImage] as? UIImage {
imageView?.image = image
dismiss(animated: true, completion: nil)
}
}
}
Usage
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = false
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
Full sample
Do not forget to add the solution code here (look above)
import UIKit
class ViewController: UIViewController {
private weak var imageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView(frame: .zero)
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(imageView)
imageView.widthAnchor.constraint(equalToConstant: 200).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 200).isActive = true
self.imageView = imageView
let button = UIButton(frame: .zero)
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(showImages), for: .touchUpInside)
stackView.addArrangedSubview(button)
}
#IBAction func showImages(_ sender: AnyObject) {
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = false
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
}
I found that the delegate code had to be within an active UIViewController.
I originally tried to have my code in a separate file, as as NSObject with the correct delegate protocols declared, like this:
class PhotoPicker: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
But that never called the delegate methods.
Taking the exact same code and placing it within the UIViewController I was calling it from made it work.
It looks like the best solution is to create a pop-up type view, and have its ViewController keep the code.
Yo have to make sure that UIImagePickerController was not released before delegate called.
I created an ImagePicker class to handle all the
UIImagePickerController stuff.
I created similar class, but
func onButtonDidTap(sender: UIButton) {
.....
let picker = VLImagePickerController()
picker.show(fromSender: sender, handler: { (image: UIImage?) -> (Void) in
if (image != nil) {
self.setImage(image!)
}
})
....
}
did not work for me.
'picker' was released before 'handler' could be called.
I created permanent reference, and it worked:
let picker = VLImagePickerController()
func onButtonDidTap(sender: UIButton) {
.....
//let picker = VLImagePickerController()
picker.show(fromSender: sender, handler: { (image: UIImage?) -> (Void) in
if (image != nil) {
self.setImage(image!)
}
})
....
}
I also faced this issue and solved it by using below solution. Set picker's delegate after present completion.
controller.present(picker, animated: true, completion: {
self.picker.delegate = self
})
Hope this will work for you!!
As per my experience, it is an issue of ARC.
If we define instance as locally then ARC will remove its reference
automatically once methods scope end. If you define globally then it
is kept in memory until the view controller is not deinitialized.
Short Answer:
Define UIImagePickerController instance globally.
Long Answer:
I have created once the common class of NSObject and delegates method of UIImagePickerController is not called.
After 5 hours of brainstorming, Finally, get the solution. It seems like an issue related to memory deallocation during the captured image from the camera.
public typealias CameraBlock = (UIImage?, Bool) -> Void
class HSCameraSingleton: NSObject {
var pickerController = UIImagePickerController()
private var completionBlock: CameraBlock!
var presentationController: UIViewController?
public init(presentationController: UIViewController) {
super.init()
self.presentationController = presentationController
}
public func present(completionBlock: #escaping CameraBlock) {
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
return
}
self.pickerController = UIImagePickerController()
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.sourceType = .camera
self.completionBlock = completionBlock
self.presentationController?.present(self.pickerController, animated: true, completion: nil)
}
}
extension HSCameraSingleton: UIImagePickerControllerDelegate,UINavigationControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.completionBlock?(nil,false)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else {
self.completionBlock?(nil,false)
return
}
self.completionBlock?(image,true)
pickerController.dismiss(animated:true, completion: nil)
}
}
class AuthViewController: UIViewController{
lazy var overlay = HSCameraSingleton(presentationController:self)
#IBAction func actionLoginTap(_ sender: UIControl) {
overlay.present { (image, status) in
print(image,status)
}
}
}
swift 4.2
Add Delegate method according ViewController
UIImagePickerControllerDelegate,UINavigationControllerDelegate
//IBOutlet
#IBOutlet weak var ImagePhoto: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
//Button Action Take Photo
#IBAction func btnPhotoTap(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary // Or .camera as you require
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
//MARK:-imagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image1 = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
self.ImagePhoto.image = image1
self.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("Cancel")
self.dismiss(animated: true, completion: nil)
}
This code works, (although, it redisplays over and over because it displays the picker in viewWillAppear, this is just to keep code small). I would look at what is different from this. It could have to do with your top view controller? Why not just display the picker from a view controller rather than go to application's top view controller? Also, once you get the delegate callback, you need to dismiss the view controller.
import UIKit
import MobileCoreServices
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.mediaTypes = [kUTTypeImage as String]
imagePicker.delegate = self
}
override func viewDidAppear(_ animated: Bool) { // keeps reopening, do not this in your code.
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
imagePicker.dismiss(animated: true, completion: nil)
}
}
I voted this one up because I was missing the UINavgationControllerDelegate declaration and this comment helped.
imagePickerController wasn't being called.
Something I found that helped me was making sure the delegate was set as public rather than private.
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
I have a class where I am calling the UIImagePickerController to select an image and return it so I can use it somewhere else in my code.
Now I set the delegate and implement the necessary function I need, but the delegate functions aren't being called.
The portion of my code that has the issue:
import UIKit
typealias PhotoTakingHelperCallback = (UIImage? -> Void)
class PhotoTakingHelper: NSObject
{
weak var viewController:UIViewController!
var callback: PhotoTakingHelperCallback
var imagePickerController: UIImagePickerController?
init(viewController:UIViewController , callback:PhotoTakingHelperCallback) {
self.viewController = viewController
self.callback = callback
super.init()
showPhotoSourceSelection()
}
func showPhotoSourceSelection() {
let alertController = UIAlertController(title: nil, message: "Where do you want to get your picture from?", preferredStyle: .ActionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
let photoLibraryAction = UIAlertAction(title: "Photo from Library", style: .Default) { (action) -> Void in
self.showImagePickerController(.PhotoLibrary)
}
alertController.addAction(photoLibraryAction)
if (UIImagePickerController.isCameraDeviceAvailable(.Rear) ) {
let cameraAction = UIAlertAction(title: "Photo from Camera", style: .Default, handler: { (action) -> Void in
self.showImagePickerController(.Camera)
})
alertController.addAction(cameraAction)
}
viewController.presentViewController(alertController, animated: true, completion: nil)
}
func showImagePickerController(sourceType: UIImagePickerControllerSourceType) {
imagePickerController = UIImagePickerController()
imagePickerController!.delegate = self
imagePickerController!.sourceType = sourceType
print("Set the delegate \(imagePickerController?.delegate?.isMemberOfClass(PhotoTakingHelper))")
self.viewController.presentViewController(imagePickerController!, animated: true, completion: nil)
}
}
extension PhotoTakingHelper: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
print("HELLO?")
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
viewController.dismissViewControllerAnimated(false, completion: nil)
print("calling the callback now " )
callback(image)
print("Finished calling the callback")
}
}
I've checked iPhone: UIImagePickerControllerDelegate methods not Invoked? and a similar problem and many more but none have solved the issue.
I've realized that none of the delegate methods are invoked as when I run through the program on the simulator and choose an image in the photo library, the console only prints.
Set the delegate Optional(true)
You're creating a PhotoTakingHelper but you're not keeping a reference to it. That is, you're doing something like this:
// In your view controller
#IBAction func pickImageButtonWasTapped() {
_ = PhotoTakingHelper(viewController: self) { image in
print("Callback!")
}
}
The UIImagePickerController keeps only a weak reference to its delegate, which is not enough to keep it alive, so you have to keep a strong reference to it. You could do it by storing the helper in an instance variable of your view controller, like this:
// In your view controller
var helper: PhotoTakingHelper?
#IBAction func pickImageButtonWasTapped() {
helper = PhotoTakingHelper(viewController: self) { image in
print("Callback!")
}
}
Or you could make the helper keep a reference to itself, like this:
// In PhotoTakingHelper
var me: PhotoTakingHelper?
init(viewController:UIViewController , callback:PhotoTakingHelperCallback) {
self.viewController = viewController
self.callback = callback
super.init()
me = self
showPhotoSourceSelection()
}
And then you need to set the reference to nil when the image picker is done, to let the helper be deallocated:
extension PhotoTakingHelper: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
viewController.dismissViewControllerAnimated(false, completion: nil)
callback(info[UIImagePickerControllerOriginalImage] as? UIImage)
me = nil
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
viewController.dismissViewControllerAnimated(false, completion: nil)
me = nil
}
}