UIImagePickerControllerDeletage is not being called - ios

I am trying to create an controller to handle all image functionality so that you can easily bind all camera actions from any view controller.
Ideally what my goal was to create a class with a function that returns an UIImage and allow my self to write individual completion handlers
ie.
let imagePicker = ImagePickerAlertController(frame:self.view.frame,controller:self)
imagePicker.displayAlert(){
imageValue in if let image = imageValue {
myImageView.image = image
}
}
However, i cannot seem to save the image or even access the image that i have taken from the camera. The imagePickerController function does not seem to be hitting.
import UIKit
class ImagePickerAlertController: UIView, UIImagePickerControllerDelegate,UINavigationControllerDelegate {
var UIViewController : UIViewController?
let imagePicker: UIImagePickerController! = UIImagePickerController()
init(frame: CGRect, controller: UIViewController){
self.UIViewController = controller
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder) {
self.UIViewController = nil
super.init(coder: aDecoder)
}
public func displayAlert(){
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let galleryAction = UIAlertAction(title: "Choose Photo",style:.default) {action -> Void in print("ok")}
let cameraAction = UIAlertAction(title: "Take Photo",style:.default) {action -> Void in self.takePicture() }
let cancelAction = UIAlertAction(title: "Cancel",style:.cancel) {action -> Void in }
alert.addAction(cancelAction)
alert.addAction(cameraAction)
alert.addAction(galleryAction)
self.UIViewController?.present(alert,animated:true,completion:nil)
}
private func takePicture() {
if (UIImagePickerController.isSourceTypeAvailable(.camera)){
if UIImagePickerController.availableCaptureModes(for: .rear) != nil || UIImagePickerController.availableCaptureModes(for: .front) != nil{
imagePicker.allowsEditing = false
imagePicker.sourceType = .camera
imagePicker.cameraCaptureMode = .photo
imagePicker.delegate = self
self.UIViewController?.present(imagePicker,animated: true,completion: nil)
}
else {
postAlert(title: "Rear camera doesn't exist",message:"Application cannot access the camera.")
}
}
else {
postAlert(title: "Camera inaccessable",message:"Application cannot access the camera.")
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("got image")
if let pickedImage:UIImage = (info[UIImagePickerControllerOriginalImage]) as? UIImage {
let selectorToCall = Selector(("imageWasSavedSuccessfully:didFinishSavingWithError:context:"))
UIImageWriteToSavedPhotosAlbum(pickedImage, self, selectorToCall, nil)
}
imagePicker.dismiss(animated: true,completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("cancel")
self.UIViewController?.dismiss(animated: true, completion: nil)
}
func imageWasSavedSuccessfully(image: UIImage, didFinishSavingWithError error : NSError!, context: UnsafeMutablePointer<()>){
print("image saved")
if (error) != nil {
print(error)
}
else {
print("good to go")
}
}
func postAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.UIViewController?.present(alert, animated: true, completion: nil)
}
}

The problem is that you try to present imagePicker when your UIViewController already has a modal view controller presented above.
displayAlert():
self.UIViewController?.present(alert,animated:true,completion:nil)
takePicture():
self.UIViewController?.present(imagePicker,animated: true,completion:
nil)
So you should dismiss UIAlertController as soon as you don't need it:
let cameraAction = UIAlertAction(title: "Take Photo",style:.default) {action -> Void in
alert.dismiss(animated: true, completion: nil)
self.takePicture()
}
Now viewController can present without any issues

Related

How to pick, display two images on two UIImageViews, and then upload these two chosen images to Firebase Database and Storage?

I want to pick two different images on two imageView's on one ViewController, display them, and after pushing the button save picked images to firebase database and storage to its particular user. My code only able to upload one picked image, not two different, I understand that the problem with UIImagePickerController part, but how can I resolve it. Full code of viewController is below. Please help!!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SettingProfileViewController.handleSelectProfileImageView(sender:)))
profilePhoto.addGestureRecognizer(tapGesture)
profilePhoto.isUserInteractionEnabled = true
let wallTapGesture = UITapGestureRecognizer(target: self, action: #selector(SettingProfileViewController.handleSelectWallpaperImageView(sender:)))
wallpaperPhoto.addGestureRecognizer(wallTapGesture)
wallpaperPhoto.isUserInteractionEnabled = true
profilePhoto.layer.cornerRadius = 60
profilePhoto.clipsToBounds = true
}
weak var activeImageView:UIImageView? = nil
#objc func handleSelectWallpaperImageView(sender: UIGestureRecognizer){
guard let sendingImageView = sender.view as? UIImageView else {
print("Ooops, received this gesture not from an ImageView")
return
}
activeImageView = sendingImageView
let actionSheet = UIAlertController(title: "New Photo", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Камера", style: .default, handler: { action in
self.showCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Альбом ", style: .default, handler: {action in
self.showAlbum()
}))
actionSheet.addAction(UIAlertAction(title: "Отмена", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
#objc func handleSelectProfileImageView(sender: UIGestureRecognizer){
guard let sendingImageView = sender.view as? UIImageView else {
print("Ooops, received this gesture not from an ImageView")
return
}
activeImageView = sendingImageView
let actionSheet = UIAlertController(title: "New Photo", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Камера", style: .default, handler: { action in
self.showCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Альбом ", style: .default, handler: {action in
self.showAlbum()
}))
actionSheet.addAction(UIAlertAction(title: "Отмена", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
func showCamera() {
let cameraPicker = UIImagePickerController()
cameraPicker.delegate = self
cameraPicker.sourceType = .camera
present(cameraPicker, animated: true, completion: nil)
}
func showAlbum(){
let cameraPicker = UIImagePickerController()
cameraPicker.delegate = self
cameraPicker.sourceType = .photoLibrary
present(cameraPicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
dismiss(animated: true, completion: nil)
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage{
// selectedImage = image
activeImageView?.image = image
// currentImage = image
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
///для того чтобы загруженные фото, отображались на ProfileViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "profileVC" {
let destination = segue.destination as! ProfileViewController
destination.wImage = activeImageView?.image
}
}
#IBAction func saveTapped(_ sender: Any) {
let db = Firestore.firestore()
let did = Auth.auth().currentUser!.uid
let storageRef = Storage.storage().reference(forURL: "gs://crut-6c67c.appspot.com").child("profile_Image").child(did)
if let pImage = self.activeImageView?.image, let imageData = pImage.jpegData(compressionQuality: 0.1) {
storageRef.putData(imageData, metadata: nil, completion: {(metadata, Error) in
if Error != nil, metadata != nil {
return
}
storageRef.downloadURL { (url: URL?,error: Error?) in
if let profileImageUrl = url?.absoluteString{
db.collection("suppliers").document("ip").collection("ipinfo").document(did).setData(["Profile Image":profileImageUrl], merge: true)
}
}
})
}
let storyBoard: UIStoryboard = UIStoryboard(name: "Profile", bundle: nil)
let profileViewController = storyBoard.instantiateViewController(identifier:profile.Storyboard.profileViewController) as? ProfileViewController
self.view.window?.rootViewController = profileViewController
self.view.window?.makeKeyAndVisible()
}
}
You can make an instance variable in your viewController i.e.
private var isProfilePhotoSelecting = true
When user tap on profileImageView in handleSelectProfileImageView method set isProfilePhotoSelecting to true i.e.
#objc func handleSelectProfileImageView(sender: UIGestureRecognizer){
guard let sendingImageView = sender.view as? UIImageView else {
print("Ooops, received this gesture not from an ImageView")
return
}
// Updated the image under consideration
isProfilePhotoSelecting = true
let actionSheet = UIAlertController(title: "New Photo", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Камера", style: .default, handler: { action in
self.showCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Альбом ", style: .default, handler: {action in
self.showAlbum()
}))
actionSheet.addAction(UIAlertAction(title: "Отмена", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
And on wallpaperImageView tapping in handleSelectWallpaperImageView method set isProfilePhotoSelecting to false i.e.
#objc func handleSelectWallpaperImageView(sender: UIGestureRecognizer){
guard let sendingImageView = sender.view as? UIImageView else {
print("Ooops, received this gesture not from an ImageView")
return
}
// Updated the image under consideration
isProfilePhotoSelecting = false
let actionSheet = UIAlertController(title: "New Photo", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Камера", style: .default, handler: { action in
self.showCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Альбом ", style: .default, handler: {action in
self.showAlbum()
}))
actionSheet.addAction(UIAlertAction(title: "Отмена", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
Then update your imagePickerDelegate to :
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
if isProfilePhotoSelecting {
profilePhoto.image = image
} else {
wallpaperPhoto.image = image
}
}
dismiss(animated: true, completion: nil)
}
This will help you set both your images to their respective imageView. Then in saveTapped(_:) method you can check both imageViews for images and upload them i.e. Your saveTapped() method should look like this
#IBAction func saveTapped(_ sender: Any) {
let db = Firestore.firestore()
let did = Auth.auth().currentUser!.uid
let storageRef = Storage.storage().reference(forURL: "gs://crut-6c67c.appspot.com")
if let profileImage = self.profilePhoto.image, let imageData = profileImage.jpegData(compressionQuality: 0.1) {
let profileStorage = storageRef.child("profile_Image").child(did)
profileStorage.putData(imageData, metadata: nil, completion: {(metadata, Error) in
if Error != nil, metadata != nil {
return
}
profileStorage.downloadURL { (url: URL?,error: Error?) in
if let profileImageUrl = url?.absoluteString {
db.collection("suppliers").document("ip").collection("ipinfo").document(did).setData(["Profile Image":profileImageUrl], merge: true)
}
}
})
}
if let wallpaperImage = self.wallpaperPhoto.image, let imageData = wallpaperImage.jpegData(compressionQuality: 0.1) {
let wallpaperStorage = storageRef.child("wallpaper_Image").child(did)
wallpaperStorage.putData(imageData, metadata: nil, completion: {(metadata, Error) in
if Error != nil, metadata != nil {
return
}
wallpaperStorage.downloadURL { (url: URL?,error: Error?) in
if let wallpaperImageUrl = url?.absoluteString {
// Do your stuff with wallpaper image url here
}
}
})
}
let storyBoard: UIStoryboard = UIStoryboard(name: "Profile", bundle: nil)
let profileViewController = storyBoard.instantiateViewController(identifier:profile.Storyboard.profileViewController) as? ProfileViewController
self.view.window?.rootViewController = profileViewController
self.view.window?.makeKeyAndVisible()
}
Note: This will not wait for the images to upload as you are waiting for them to upload. But it depends on your usecase if you want to wait until the image uploading completes and then move to ProfileViewController, you can use DispatchGroup for that purpose.
I hope your saving the last choosen image on activeImageView and passing that to Firebase, hence only one image is getting uploaded.
Instead create and Array and both the images choosen from handleSelectWallpaperImageView and handleSelectWallpaperImageView, then loop through the Array and send to Firebase.
Check this for more information: https://stackoverflow.com/a/49934285/1244403
Created option Boolean variable
var isProfilePhotoSelecting:Bool?
#objc func handleSelectWallpaperImageView(sender: UIGestureRecognizer){
isProfilePhotoSelecting = false
///
other code
}
#objc func handleSelectProfileImageView(sender: UIGestureRecognizer){
isProfilePhotoSelecting = true
///
other code
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
dismiss(animated: true, completion: nil)
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage{
if isProfilePhotoSelecting == true {
profilePhoto.image = image
}else {
wallpaperPhoto.image = image
}
}
}
In the saveTapped need to be added storage references to each chosen image and its respective database.
#IBAction func saveTapped(_ sender: Any) {
let db = Firestore.firestore()
let did = Auth.auth().currentUser!.uid
let storageRef = Storage.storage().reference(forURL: "gs://crut-6c67c.appspot.com").child("profile_Image").child(did)
if let profileImage = self.profilePhoto.image, let imageData = profileImage.jpegData(compressionQuality: 0.1){
storageRef.putData(imageData, metadata: nil, completion: {(metadata, Error) in
if Error != nil, metadata != nil {
return
}
storageRef.downloadURL { (url: URL?, error: Error?) in
if let profileImageUrl = url?.absoluteString {
db.collection("suppliers").document("ip").collection("ipinfo").document(did).setData(["Profile Image":profileImageUrl], merge: true)
}
}
})
}
let wallStoreRef = Storage.storage().reference(forURL: "gs://crut-6c67c.appspot.com").child("Wallpaper_Image").child(did)
if let wallpaperImage = self.wallpaperPhoto.image, let imageData = wallpaperImage.jpegData(compressionQuality: 0.1) {
wallStoreRef.putData(imageData, metadata: nil, completion: {(metadata, Error) in
if Error != nil, metadata != nil {
return
}
wallStoreRef.downloadURL { (url: URL?,error: Error?) in
if let wallpaperImageUrl = url?.absoluteString {
// Do your stuff with wallpaper image url here
db.collection("suppliers").document("ip").collection("ipinfo").document(did).setData(["Wallpaper Image":wallpaperImageUrl], merge: true)
}
}
})
}

Delegate retention circle although using weak

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)

Value of type 'Binder<UIImage?>' has no member 'bind'

I want to bind my ImageView to viewModel for save my selected image to Core Data.
My code look like this:
class FoodViewModel: FoodViewModelType {
var foodImage: BehaviorRelay<UIImage?>
//... another code
}
My controller:
class NewFoodViewController: UIViewController {
#IBOutlet weak var foodImageView: UIImageView!
override func viewDidLoad() {
//... another code
self.foodImageView.rx.image.bind(to: foodViewModel.foodImage).disposed(by: self.disposeBag)
}
}
And i get error:
Value of type Binder < UIImage? > has no member 'bind'
How to save my image to Core Data with good MVVM practice?
Update
I am choose photo in view controller:
func chooseImagePickerAction(source: UIImagePickerController.SourceType) {
if UIImagePickerController.isSourceTypeAvailable(source) {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = source
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}
}
#objc func foodImageViewTapped(_ sender: AnyObject) {
let alertController = UIAlertController(title: "Photo path", message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) { (action) in
self.chooseImagePickerAction(source: .camera)
}
let photoLibAction = UIAlertAction(title: "Photo", style: .default) { (action) in
self.chooseImagePickerAction(source: .photoLibrary)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alertController.addAction(cameraAction)
alertController.addAction(photoLibAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
extension NewFoodViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let info = convertFromUIImagePickerControllerInfoKeyDictionary(info)
foodImageView.image = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage
foodImageView.contentMode = .scaleAspectFill
dismiss(animated: true, completion: nil)
}
private func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] {
return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)})
}
private func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String {
return input.rawValue
}
}
And in viewDidLoad (without image):
saveNewFoodBarButtonItem.rx.tap.subscribe(onNext: { [weak self] _ in
guard let self = self else { return }
let foodViewModel = FoodViewModel()
self.foodQuantityTypeTextField.rx.text.bind(to: foodViewModel.foodQuantityType).disposed(by: self.disposeBag)
self.foodShelfLifeTextField.rx.text.bind(to: foodViewModel.foodShelfLife).disposed(by: self.disposeBag)
self.foodCategoryTextField.rx.text.bind(to: foodViewModel.foodCategoryId).disposed(by: self.disposeBag)
self.foodQuantityTextField.rx.text.bind(to: foodViewModel.foodQuantity).disposed(by: self.disposeBag)
self.foodNameTextField.rx.text.bind(to: foodViewModel.foodName).disposed(by: self.disposeBag)
foodViewModel.saveNewFood(fridgeViewModel: self.fridgeViewModel!)
self.dismiss(animated: true)
}).disposed(by: disposeBag)
UIImageView is not bindable because it is an output view, not an input view, i.e., you push things into it, it doesn't push things out.
In order to emit an image to your view model, you need to do it the at the point where you push the image into the UIImageView.

Unable to pass image taken from camera to another viewcontroller

I have a button to take an image . I wish to pass the image taken from camera to another view controller.The code for first viewcontroller is shown below.
#IBAction func takePhoto(sender: AnyObject)
{ let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .Camera
presentViewController(picker, animated: true,completion : nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
TakenImage = info[UIImagePickerControllerOriginalImage] as? UIImage ; dismissViewControllerAnimated(true, completion: nil )
let controller = self.storyboard!.instantiateViewControllerWithIdentifier("NoteDetailViewController") as! NoteDetailViewController
controller.takinPhoto = true
if (note != "")
{
controller.content = note
}
controller.imageFromCamera = TakenImage
if (self.tags != "")
{
controller.tagsTextField.text = self.tags
}
self.presentViewController(controller, animated: true, completion: nil)
}
#IBAction func save(sender: AnyObject)
{
UIImageWriteToSavedPhotosAlbum(TakenImage!, self, "image:didFinishSavingWithError:contextInfo:", nil)
}
func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo:UnsafePointer<Void>)
{
if error == nil {
let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
} else
{
let ac = UIAlertController(title: "Save error", message: error?.localizedDescription, preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}
}
The code for my second viewcontroller is shown below
if (takinPhoto == true)
{
if (imageFromCamera != nil)
{
if let image1 = self.imageFromCamera
{
self.imageView2.image = image1
}
}
if (self.content != "")
{
self.contentTextField2.text = content
}
}
But image from camera is not appearing in the second viewcontroller.How can I solve this??
Updated answer.
I rewrote your code and was able to get it working locally. The main change was to use a different delegate method which is specific to photos.
#IBAction func takePhoto(sender: AnyObject) { let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .Camera
presentViewController(picker, animated: true,completion : nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
takenImage = image
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func save(sender: AnyObject) {
UIImageWriteToSavedPhotosAlbum(takenImage!, self, #selector(ViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
}
func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo:UnsafePointer<Void>) {
if error == nil {
let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
} else {
let ac = UIAlertController(title: "Save error", message: error?.localizedDescription, preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "saveTakenImage") {
let controller = segue.destinationViewController as! NoteDetailViewController
controller.takinPhoto = true
if (note != "") {
controller.content = note
}
controller.imageFromCamera = takenImage
if (self.tags != "") {
controller.tagsTextField.text = self.tags
}
}
}

How to fetch camera based and media based images/videos separately to display in collection view in iOS device

I am developing a chat app. In my app when I click attachement button, two options should come.
1) images/videos captured by the device camera(not capturing image at that time. Fetch images taken by the camera that is stored in the device)
2) images/videos downloaded from the web or other medias
Is there any way to fetch images/videos according to the above given criteria preferably using assets library
class YourController : UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIActionSheetDelegate,UIPopoverPresentationControllerDelegate
{
override func viewDidLoad() {
super.viewDidLoad()
}
func takePhotoByGalleryOrCamera(){
//MAark Take picture from gallery and camera
//image picker controller to use take image
// uialert controller to make action
let imageController = UIImagePickerController()
imageController.editing = false
imageController.delegate = self;
let alert = UIAlertController(title: "", message: "Profile Image Selctor", preferredStyle: UIAlertControllerStyle.ActionSheet)
let libButton = UIAlertAction(title: "Select photo from library", style: UIAlertActionStyle.Default) { (alert) -> Void in
imageController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(imageController, animated: true, completion: nil)
}
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
let cameraButton = UIAlertAction(title: "Take a picture", style: UIAlertActionStyle.Default) { (alert) -> Void in
print("Take Photo")
imageController.sourceType = UIImagePickerControllerSourceType.Camera
self.presentViewController(imageController, animated: true, completion: nil)
}
alert.addAction(cameraButton)
} else {
print("Camera not available")
}
let cancelButton = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) { (alert) -> Void in
print("Cancel Pressed")
}
alert.addAction(libButton)
alert.addAction(cancelButton)
if UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad {
alert.modalPresentationStyle = UIModalPresentationStyle.Popover;
// alert.transitioningDelegate = self;
alert.popoverPresentationController!.sourceView = self.view;
alert.popoverPresentationController!.sourceRect = CGRectMake(0, SizeUtil.screenHeight(), SizeUtil.screenWidth(), SizeUtil.screenHeight()*0.4)
alert.popoverPresentationController!.delegate = self;
self.presentViewController(alert, animated: true, completion: nil)
} else {
self.presentViewController(alert, animated: true, completion: nil)
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.Popover
}
//image picker Delegate
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
self.dismissViewControllerAnimated(true, completion: nil)
let header = profileTableView.headerViewForSection(0) as! ProfileHeaderView
header.btnImage.setImage(image, forState: UIControlState.Normal)
_userProfileImage = image
}
}

Resources