How to enable a currently disabled button in Swift - ios

I have an action button in my iOS application (using Swift 3), which I guess is technically a toolbar item.
I presently disable the button in viewWillAppear() with
actionButton.isEnabled = false
I then try to enable it after an image has been selected:
func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
imageView.image = image
imageView.contentMode = .scaleAspectFit
}
dismiss(animated: true, completion: nil)
actionButton.isEnabled = true
print("selected an image in picker")
}
And I can see that my print statement occurs as well as the image being selected and activity view controller dismissing, just as expected. HOWEVER, my actionButton stays disabled.
Any ideas why? I'm very new to Swift!

viewWillAppear() is called every time, before your view controller loads. When you dismiss your imagePicker, and go back to presenting your original viewController that viewWillAppear() would be called again, and it would override your actionButton.isEnable = true to being false again. If I were you, I would disable the button in viewDidLoad(), which is called only once.

try to execute the code that changes the UI in the main thread like this:
DispatchQueue.main.async {
actionButton.isEnabled = true
}

Related

How To Perform Segue After Picking Image

I would like to perform a segue just after the user chooses an image (by clicking on the image view).
I have already checked the segue id and successfully managed to connect both view controllers.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// Executed when we select an image from the photo library.
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
// Sets the image to the image view (not really relevant because
// we push the user to the DetailVC, but let's leave it).
self.imageView.image = image
// Closing the image picker.
self.dismiss(animated: true, completion: nil)
// Performing segue (It doesn't perform!).
print("It gets to this point, but doesn't perform.")
self.performSegue(withIdentifier: "showDetail", sender: nil)
}
}
The program sets the image to the image view, gets to that print statement just after the "perform" function, but the segue is not performed.
in storyboard select the view controller which has the imageview, at the top there is yellowcircle right click drag from there to the view controller you want to segue to and give its identifier an ID = “segueID”
then edit your didfinishpicking method like this
if let img = info[.originalImage] as? uiimage{
myimageview.image = img
dismiss(animated: true) {
self.performSegue(withIdentifier: “segueID”, sender: self)}}
I believe the problem lies that you are dismissing your view/view controller before performing segue
// Closing the image picker.
self.dismiss(animated: true, completion: nil)
Have you tried:
Setting the sender to self?
Making sure that your segue is the correct type. (I know you checked the name and you probably would have gotten an error if you got the name wrong, in this case I mean "push" vs "show detail" etc)
Adding a prepareForSegue function to make sure that the segue is actually getting called
Building up on #Loren Rogers answer:
// Closing the image picker.
self.dismiss(animated: true, completion: nil)
You're not actually dismissing the UIImagePickerController. Like Loren mentioned, you're dismissing the ViewController. This is because self is a reference to the ViewController.
Replace self with picker like so:
// Closing the image picker.
picker.dismiss(animated: true, completion: nil)

Input Accessory View disappear after Image Picker is cancelled [Swift]

I have a pretty standard inputAccessoryView with image and album buttons. But I found that if album is launched when keyboard is shown (in other words, textView is the first responder), inputAccessoryView disappears when photo album is dismissed. How to make it stay at the bottom of the screen?
My implementation is generic:
let inputContainerView: UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 40)
override var inputAccessoryView: UIView? {
return inputContainerView
}
lazy var imagePicker: UIImagePickerController = {
let picker = UIImagePickerController()
picker.delegate = self
return picker
}()
// The function that is hooked up to the album button
func handleAlbum() {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = false
present(imagePicker, animated: true, completion: nil)
}
}
Here is a gif demonstrating the problem. Notice the inputAccessoryView disappeared at the end. Thank you so much!
I solved it. It turns out I need to ask textView to resignFirstResponder() before presenting the image picker. Also, add the following code. It handles the case when user cancelled the pick operation.
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
Per Apple documentation:
Your delegate’s implementation of this method should dismiss the picker view by calling the dismissModalViewControllerAnimated: method of the parent view controller.
Implementation of this method is optional, but expected.
The dismissModalViewControllerAnimated: method is deprecated and should use dismissViewControllerAnimated:completion: instead.
Try adding self.inputAccessoryView.becomeFirstResponder() after image picker is dismissed.

UICollectionView not showing images after unhiding it

I have an UICollectionView that starts hidden in the viewDidLoad, then when an image is added with the UIImagePicker delegate the collection goes "unhidden" and then reloads, here is the code below
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
self.imagesToSend.append(pickedImage)
}
self.imagenesCollectionView.isHidden = false
self.imagenesCollectionView.reloadData()
dismiss(animated: true, completion:nil)
}
the problem is that when the collection shows it doesn't show any image, I have to add a second image so the image can show, I want the collection to show when there are images and to hide when there aren't images, until now the hiding functionality its working fine, but not the showing one.
Try adding
self.imagenesCollectionView.isHidden = false
self.imagenesCollectionView.reloadData()
in dismiss completion handler. Like
dismiss(animated: true, completion:{
self.imagenesCollectionView.isHidden = false
self.imagenesCollectionView.reloadData()
})
Edit 1:
Instead of reloading collection view in completion. add collection view reload in viewWillAppear method based on the array data availability.
eg:
if self.imagesToSend.count > 0 {
self.imagenesCollectionView.isHidden = false
self.imagenesCollectionView.reloadData()
} else {
self.imagenesCollectionView.isHidden = true
}
Try to use the methods setNeedsLayout or setNeedsDisplay. They are related to the view update, and maybe you need to use them on the collectionView or on the imageView itself.

How to dismiss a view controller in a navigation controller, without dismissing the whole stack

Basically I have tab bar controller with two tabs, each holds a separate navigation controller, each with a couple of views (see below).
In the "top" navigation controller (held in the right tab), I can choose an image, and then am taken to the rightmost screen to preview the image...
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
let previewController = self.storyboard?.instantiateViewController(withIdentifier: "previewVC") as! PreviewViewController
previewController.img = image
previewController.navigationItem.setHidesBackButton(true, animated: false)
self.navigationController?.pushViewController(previewController, animated: true)
self.dismiss(animated: true, completion: nil)
}
}
...where I tap "post". This uploads the image and calls self.tabBarController?.selectedIndex = 0
That simply takes me to the image feed, which is in the "bottom" navigation controller (left tab). So far so good. However, the preview screen is still displayed in the "right" tab, when I need the upload screen to be displayed after I post.
If I call self.dismiss(animated: true, completion: nil) when I tap post, the entire navigation controller is dismissed and I'm taken back to the login screen.
So I'm trying to dismiss the preview controller so the upload controller is shown as the default view in the right tab, without dismissing the entire stack in that navigation controller.
How can I do this?
If you use navigation controllers to add view controllers to the navigation stack (push), you can remove them by calling a pop function. When you present view controllers modally, you call dismiss to remove the view controller.
The delegate function gives you the (image picker) view controller and you should dismiss that, not the view controller that is the delegate (usually the presenting view controller). Maybe try something like this:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
let previewController = self.storyboard?.instantiateViewController(withIdentifier: "previewVC") as! PreviewViewController
previewController.img = image
previewController.navigationItem.setHidesBackButton(true, animated: false)
self.navigationController?.pushViewController(previewController, animated: true)
picker.dismiss(animated: true, completion: nil)
}
}

Unbalanced calls to begin/end appearance transitions for <ViewController>

I get the error:
"Unbalanced calls to begin/end appearance transitions for ”.
In my app you start at a tableViewController, if you click the photo icon at the bottom the camera pops up and when you have taken a photo you go through a navigationController into the editing viewController. From here if you click the image you are sent to the preview viewController.
My problem is if I click "New Photo" when editing an existing item it works fine, but if I click "New Photo" on an item i have just created (and not yet saved) I'm sent all the way back to the initial tableViewController
My code looks like this:
// Called when the user finishes taking a photo.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// The info dictionary contains multiple representations of the image, and this uses the original.
image = info[UIImagePickerControllerOriginalImage] as? UIImage
// Dismiss the picker.
dismiss(animated: false, completion: nil)
performSegue(withIdentifier: "unwindToExpenseView", sender: self)
}
In my editing viewController
#IBAction func unwindToExpenseView(sender: UIStoryboardSegue) {
if let ImagePreviewer = sender.source as? ImagePreviewViewController {
photoImageView.image = ImagePreviewer.image
}
}
Try to performSegue in completion of dismiss(animated:).
self.dismiss(animated: false) {
performSegue(withIdentifier: "unwindToExpenseView", sender: self)
}

Resources