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)
Related
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
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 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.
What I'm trying to do is present an alert, where the user eaither presess get photo from camera, or photolibrary. However, when I press library, the UIImagePickerViewController doesn't present. I am adding UIAlert actions to the ALert, and the handler of which I am passing in is as a function which presents the ImagePicker controller. What am I doing wrong? I suspect it has to do with how I am presenting the ImagePickerViewController, but I'm not sure. I read the apple docs and they said that a popover is the only style to present it.
import UIKit
import Firebase
class SignupViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var ProfilePic: UIButton!
//When button is pressed, alert will pop up
#IBAction func bringUpAlert(_ sender: Any) {
let alert = UIAlertController(title: "Profile Photo", message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: {(action) -> Void in
func showCamera(){
let camera = UIImagePickerController()
UIImagePickerController.isSourceTypeAvailable(.camera)
camera.delegate = self
camera.sourceType = .camera
self.present(camera, animated: true, completion: nil)
}
}))
alert.addAction(UIAlertAction(title: "Library", style: .default, handler: {(action) -> Void in
func showLibrary(){
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let library = UIImagePickerController()
library.delegate = self
library.sourceType = .photoLibrary
library.modalPresentationStyle = UIModalPresentationStyle.popover
self.present(library, animated: true, completion: nil)
}
func imagePickerController(_picker: UIImagePickerController, didFinishPickingMediaWithInfo: [String:Any]) {
let selectedImage = didFinishPickingMediaWithInfo[UIImagePickerControllerOriginalImage] as! UIImage
self.ProfilePic.setImage(selectedImage, for: .normal)
}
}
}))
self.present(alert, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
//Customizing Profile Pic View
ProfilePic.layer.cornerRadius = ProfilePic.frame.size.width / 2
ProfilePic.clipsToBounds = true
ProfilePic.layer.borderWidth = 3
ProfilePic.layer.borderColor = UIColor.gray.cgColor
}
}
The image picker is never shown. In the action alert, the function showCamera is never called.
The solution is to remove the function.
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: {(action) -> Void in
let camera = UIImagePickerController()
UIImagePickerController.isSourceTypeAvailable(.camera)
camera.delegate = self
camera.sourceType = .camera
self.present(camera, animated: true, completion: nil)
}))
Or, move the showCamera function declaration outside and call it from the alert action.
func showCamera(){
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let camera = UIImagePickerController()
camera.delegate = self
camera.sourceType = .camera
self.present(camera, animated: true, completion: nil)
}
}
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: {(action) -> Void in
showCamera()
}))
The same is needed of the embedded showLibrary function. And the image picker delegate method also needs to be moved to the top-level of the class.
Final class:
class SignupViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var ProfilePic: UIButton!
//When button is pressed, alert will pop up
#IBAction func bringUpAlert(_ sender: Any) {
let alert = UIAlertController(title: "Profile Photo", message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: {(action) -> Void in
self.showCamera()
}))
alert.addAction(UIAlertAction(title: "Library", style: .default, handler: {(action) -> Void in
self.showLibrary()
}))
self.present(alert, animated: true, completion: nil)
}
func showCamera(){
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let camera = UIImagePickerController()
camera.delegate = self
camera.sourceType = .camera
self.present(camera, animated: true, completion: nil)
}
}
func showLibrary(){
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let library = UIImagePickerController()
library.delegate = self
library.sourceType = .photoLibrary
library.modalPresentationStyle = UIModalPresentationStyle.popover
self.present(library, animated: true, completion: nil)
}
}
func imagePickerController(_picker: UIImagePickerController, didFinishPickingMediaWithInfo: [String:Any]) {
let selectedImage = didFinishPickingMediaWithInfo[UIImagePickerControllerOriginalImage] as! UIImage
self.ProfilePic.setImage(selectedImage, for: .normal)
}
override func viewDidLoad() {
super.viewDidLoad()
//Customizing Profile Pic View
ProfilePic.layer.cornerRadius = ProfilePic.frame.size.width / 2
ProfilePic.clipsToBounds = true
ProfilePic.layer.borderWidth = 3
ProfilePic.layer.borderColor = UIColor.gray.cgColor
}
}
Example 1:
func doStuff(x: Int, handler: (Int) -> Void) {
handler(x)
}
doStuff(x: 3) {val in
print(val)
}
--output:--
3
Example 2:
func doStuff(x: Int, handler: (Int) -> Void) {
handler(x)
}
doStuff(x: 3) {val in
func calc(a: Int, b: Int) {
print(a + b)
}
}
--output:--
<Nothing>
iOS programming isn't really for beginning programmers--it's hard for experienced programmers.
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