Camera view of UIImagePickerController frozen when navigating back to it - ios

In the delegate of UIImagePickerController when an image is taken with the camera, another view is pushed onto the navigation stack:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
picker.pushViewController(otherViewController, animated: true)
}
In the otherViewController the navigation bar is made visible:
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(false, animated: false)
}
When the < Back button in the navigation bar is tapped, the navigation bar becomes invisible again, the camera view appears, but the camera image is frozen and tapping the bottom bar buttons has no effect.
Why is that?

A workaround is not to offer the user to navigate back by replacing the Back button with a Cancel button. That dismisses the UIImagePickerController and automatically dismisses all higher views on the navigation stack, including the otherViewController.
// Replace `Back` button with `Cancel` button in the `otherViewController`
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(self.cancelButtonTapped))
#objc func cancelButtonTapped() {
// Dismiss the `UINavigationController`, e.g. by calling a delegate function
// ...
}
As a result the user would have to start the process again from the beginning instead of just navigating back.

You are directly pushing A new view before dismissing picker you had presented this is why when you come back your camera image picker is still on stack as its not dismissed
dismiss(animated:true, completion: nil)
after this push your new View as you want. didFInish is used to get the result and dismiss the picker you used pass image selected or clicked to new controller if required but dismissing to picker is required

Related

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

Present a ViewController, dismiss it, and then segue into another View

I'm trying to do the following:
User presses button
UIImagePickerController pops up
User selects image
UIImagePickerController is dismissed
Segue into another ViewController
As of now, I have it set up that when the user presses a button, a UIImagePickerController is initialized and presented with presentViewController. It is also delegated to the class that this button is linked to as well. After the user picks an image, imagePickerController is called, and dismissViewControllerAnimated is also called. Then, performSegueWithIdentifier is called. However, this last step throws the following warning, and then does not segue:
2016-09-15 03:45:44.870 Food Diary[9941:1540144] Warning: Attempt to present
on whose view is not in the window hierarchy!
I am very new to Swift and iOS development, what does this mean and how do I fix it?
Perform segue on completion of dismiss of UIImagePickerController
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
// your code
self.dismissViewControllerAnimated(true, completion: {
self.performSegueWithIdentifier("SegueIdentifier", sender: self)
})
}
Note: With sender I have passed current Controller object you can any object that you want.
Try to using these two lines code during presenting and dissmising your view controller
Presenting :
self.view.window.rootviewcontroller.presentViewController("yourVC",animated:true, nil)
Dissmising :
self.view.window.rootViewController.dismissViewController(animated:true completion:nil)
It stays on the same ViewController because the UIImagePickerController is still in view hierarchy a the d second ViewController cannot be added on top of UIImagePickerController. To fix, you need to wrap your segue in the completion of UIImagePickerCopntroller dismissal like this:
self.dismiss(animated: true, completion: {
self.performSegue(withIdentifier: "StartToMain", sender: nil)
})

Back button hidden in navigation bar (but still works)

I have read about similar problems where the problem was caused by having multiple navigation controllers, but I only have one. This is my navigation flow.
VC = UIViewController, NC = UINavigationController
VC1 -modal-> NC -root-> VC2 -show-> VC3
VC1 is not embedded in a navigation controller, I'm starting that modal segue using performSegueWithIdentifier:sender:.
VC2 then is using a show segue to present VC3, which is the one where the back button is not visible. It still works though. But, it does appear if I exit to the home screen and then enters the app again, as shown here:
https://gfycat.com/VelvetyThisHamster.
Any ideas why this is happening?
edit: To make things clear: I want the button both visible and functioning (it's not that it's working that's the problem, but that it's hidden)
EDIT 2:
If I change my navigation flow to this
NC -root-> VC2 -show-> VC3
then the back button works as intended. So the question is, how can I add a regular view controller without a navigation controller before the first navigation controller? I want it before because VC1 should not have a navigation bar and VC2 should be presented modally.
try this
Hidden
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
//use this
self.navigationItem.setHidesBackButton(true, animated: false)
//else use this
self.navigationItem.leftBarButtonItem = nil
}
Show
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
//use this
self.navigationItem.setHidesBackButton(false, animated: false)
//else
self.navigationController.navigationItem.backBarButtonItem.enabled = TRUE
}
Update
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
//use this
self.navigationItem.setHidesBackButton(false, animated: false)
//else
let backButton = UIBarButtonItem(title: "leftbutton", style: UIBarButtonItemStyle.Plain, target: self, action: "buttonMethod")
self.navigationItem.leftBarButtonItem = backButton
}
func buttonMethod() {
print("Perform action")
}
I think I found the source of the problem, so I'll post it here in case someone else runs into the same problem.
The modal presentation between VC1 and NC was made from a background queue (by calling performSegueWithIdentifier:sender: in the completion handler of an NSURLSessionDataTask to be precise). By dispatching that line of code to the main queue the problem seems to disappear.
Turn out, I had NavigationBar tint color set to "Clear". Once I changed it, the back button appeared.

How to "De-initialize" ViewController

In the first screenshot, LatestPost is set as Is Initial View Controller. When I press Camera tab, it launches the .Camera for UIImagePickerController. However when I hit "Cancel", it dismisses the current ViewController, and my tab bar goes missing as shown on the second screenshot.
The second screenshot is embedded inside a UINavigationController.
I quite certain that has got to do with dismissing my ViewController. The question here is how do I "de-initialized" my ViewController?
My codes as follow:
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}
EDIT: Added Storyboard screenshot as requested.

iOS UINavigationController barHideOnTapGestureRecognizer and UIButton interference

I have developed an app that makes use of the iOS8 feature to show or hide the navigation bar on a tap of the view.
However, the main view contains a UIButton which also act upon taps. The problem is that both 'objects' are receiving the tap and if I tap the button, the navigation bar toggles its visibility.
I can get to the barHideOnTapGestureRecognizer via the navigation controller but not really sure what can be done with it to stop it responding if a button is tapped.
Is there a way (apart from switching off or changing to 'Swipe to Hide') to subdue the navigation bar's appearance/disappearance when a button is pressed?
Don't use the standard barHideOnTapGestureRecognizer. Fortunately, it's not hard to roll your own:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let gestureRecognizer = UITapGestureRecognizer(target: self, action: "toggleBarsOnTap:")
self.view.addGestureRecognizer(gestureRecognizer)
}
func toggleBarsOnTap(sender: AnyObject?) {
let hidden = !self.navigationBarHidden
self.setNavigationBarHidden(hidden, animated: true)
self.setToolbarHidden(hidden, animated: true)
}
Taps on the view will show/hide the bars, and taps on controls (subviews of the view) will not.
[self.navigationController setNavigationBarHidden:YES];

Resources