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
Related
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
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 trying to upload a video from my library to Firebase storage in my application, but it seems to be resulting in an error. This does not happen if the video has been captured using UIImagePicker, it only happens when the user pickes an already taken video. I am using XCode 11.0 beta (11M336w) and iOS 13 beta 3.
My ImagePicker class:
import UIKit
import AVFoundation
import CropViewController
public protocol ImagePickerDelegate: class {
func didSelect(image: UIImage?)
func didSelect(fileUrl: URL)
}
open class ImagePicker: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CropViewControllerDelegate {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
private var editing = false
private var croppingStyle: CropViewCroppingStyle!
public init(presentationController: UIViewController, delegate: ImagePickerDelegate, editing: Bool, croppingStyle: CropViewCroppingStyle, mediaTypes: [String]?) {
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.editing = editing
self.croppingStyle = croppingStyle
if let mediaTypes = mediaTypes {
self.pickerController.mediaTypes = mediaTypes
} else if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .camera) {
self.pickerController.mediaTypes = mediaTypes
}
self.pickerController.sourceType = .camera
self.pickerController.showsCameraControls = true
self.pickerController.videoQuality = .typeHigh
self.pickerController.navigationBar.titleTextAttributes = nil
self.pickerController.navigationItem.rightBarButtonItem?.setTitleTextAttributes(nil, for: .normal)
self.pickerController.navigationItem.rightBarButtonItem?.setTitleTextAttributes(nil, for: .highlighted)
}
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: "Take photo or video") {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: "Choose from library") {
alertController.addAction(action)
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
cancel.setValue(UIColor.red, forKey: "titleTextColor")
alertController.addAction(cancel)
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?) {
self.delegate?.didSelect(image: image)
controller.dismiss(animated: true, completion: nil)
}
private func pickerController(_ controller: UIImagePickerController, didSelectVideo videoUrl: URL) {
self.delegate?.didSelect(fileUrl: videoUrl)
controller.dismiss(animated: true, completion: nil)
}
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(picker, didSelect: nil)
}
var picker: UIImagePickerController!
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let videoUrl = info[.mediaURL] as? URL {
self.pickerController(picker, didSelectVideo: videoUrl)
return
}
if let image = info[.originalImage] as? UIImage {
if editing {
let cropViewController = CropViewController(croppingStyle: croppingStyle, image: image)
cropViewController.delegate = self
self.picker = picker
self.presentationController?.present(cropViewController, animated: true, completion: nil)
} else {
self.pickerController(picker, didSelect: image)
}
} else {
self.pickerController(picker, didSelect: nil)
}
}
public func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.pickerController(picker, didSelect: image)
}
}
I present the viewcontroller with the following function inside my UITableView class (I just call this function when a button has been pressed):
func choose() {
let imagePicker = ImagePicker(presentationController: self, delegate: self, editing: false, croppingStyle: .default, mediaTypes: nil)
imagePicker.present(from: view)
}
Then in the imagepicker delegate I use this code:
func didSelect(image: UIImage?) {
//
}
func didSelect(fileUrl: URL) {
let uuid = NSUUID().uuidString
let path = uuid + "." + fileUrl.pathExtension
let uploadTask = Storage.storage().reference(withPath: "message_videos/\(path)").putFile(from: fileUrl, metadata: nil) { (metadata, error) in
if let error = error {
print(error)
return
}
print("success!")
}
}
When executed, this results in the following console prints:
2019-07-05 23:41:31.967623+0200 APPLICATION_NAME[9491:1007368] Failed to issue sandbox extension for file file:///private/var/mobile/Containers/Data/PluginKitPlugin/8B54AF9B-6618-4341-85DE-2EAC29198B17/tmp/trim.FEF41C1D-7D43-4E95-84A1-36AC99FEDE14.MOV, errno = 1
2019-07-05 23:41:31.982082+0200 APPLICATION_NAME[9491:1007368] Task <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1> finished with error [-1] Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>, NSLocalizedDescription=unknown error}
Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={bucket=XXXXXXXXXXXXXXXXXXXXXXX.appspot.com, _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>, object=message_videos/BB2CB773-982B-4800-9398-B2F96ED91B6D.MOV, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>"
), NSLocalizedDescription=An unknown error occurred, please check the server response., ResponseErrorDomain=NSURLErrorDomain, NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, ResponseErrorCode=-1}
Is there a way to resolve this?
So I was able to solve this issue in my app by converting the video to Data and uploading that using the putData function from Firebase Storage API.
I'm not exactly sure why this change was necessary with iOS 13, but I hope this helps.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//ensure user picked a video and you have the URL
guard let mediaType = info[.mediaType] as? String, mediaType == "public.movie", let movieURL = info[.mediaURL] as? URL else {
dismiss(animated: true)
return
}
uploadVideo(url: movieURL)
//continue code your here...
}
private func uploadVideo(url: URL) {
do {
//convert video to Data
let videoData = try Data(contentsOf: movieURL)
//build storage "path"
let uniqueID = UUID().uuidString
let path = "message_videos/" + uniqueID
let storageRef = Storage.storage().reference().child(path)
//use metadata to specify it's a video
let uploadMetadata = StorageMetadata()
uploadMetadata.contentType = "video/quicktime"
//upload via putData
let uploadTask = storageRef.putData(videoData, metadata: uploadMetadata) { (metadata, error) in
if let error = error {
//handle upload error
} else {
//successful upload
}
} catch {
//handle video -> Data error
}
}
}
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 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 !.