Swift - UIImagePickerController - how to use it? - ios

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 !.

Related

How to save Image in String to Realm from UIImagePickerController?

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

UIImagepickercontroller - Camera is not working

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

didFinishPickingMediaWithInfo is not calling in Xcode 12

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

malloc_error when presenting imagepicker controller

I'm having an issue attempting to debug a malloc error/memory leak. Heres what my code looks like:
class UploadPostController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, TOCropViewControllerDelegate {
var myPickerController : UIImagePickerController?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
setupNavBarButtons()
}
func setupNavBarButtons() {
let uploadBarButtonItem = UIBarButtonItem(title: "Upload", style: .plain, target: self, action: #selector(displayImagePickerButtonTapped))
navigationItem.rightBarButtonItem = uploadBarButtonItem
}
func displayImagePickerButtonTapped() {
myPickerController = UIImagePickerController()
myPickerController?.delegate = self
myPickerController?.sourceType = UIImagePickerControllerSourceType.photoLibrary
self.present(myPickerController!, animated: true, completion: nil) // crashes here.
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage{
let cropVC = TOCropViewController(image: pickedImage)
cropVC.delegate = self
cropVC.aspectRatioPickerButtonHidden = true
cropVC.aspectRatioPreset = .presetSquare
cropVC.aspectRatioLockEnabled = true
cropVC.resetAspectRatioEnabled = false
myPickerController?.pushViewController(cropVC, animated: true)
}
}
func cropViewController(_ cropViewController: TOCropViewController, didCropToImage image: UIImage, rect cropRect: CGRect, angle: Int) {
self.arrayOfCroppedImages.append(image)
dismiss(animated: true, completion: nil)
myPickerController = nil
}
}
I've took a lot of the code out of the file as I can't see any of the other stuff being relevant. It crashes when I'm attempting to present the UIImagePickerController
I have set a malloc_error_break breakpoint and this is what I get back
The crash is completely random. Probably around a 15% chance of occurring

UIImagePickerController delegate not called Swift 3

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])

Resources