I'm in the process of implementing a cancel button for one of my view controllers. This view controller can either be shown, or presented modally. The cancel button works fine when the view controller has been shown, but it is not having any effect when the VC has been presented modally. I have a line of code for my cancel button:
let isPresentingInAddRoutineMode = presentingViewController is UINavigationController
This line of code is supposed to distinguish whether the VC has been shown or presented. I got the line from the Apple development website. In the cancel function it then reads:
if isPresentingInAddRoutineMode {
dismiss(animated: true, completion: nil)
}
According to the website, the variable should be reading true as the VC has been presented modally (The segue in storyboard says present modally). I tried debugging to see if the boolean is returning true, but it is not. I'm very confused by this and would be very grateful if anyone had any ideas what I've done wrong here.
Thanks.
Perhaps this could be of use instead: isBeingPresented
From: https://developer.apple.com/documentation/uikit/uiviewcontroller/2097564-isbeingpresented
Related
My app starts off with a Tab Bar Controller on the first screen after logging in:
Later on the user can tap to get back to that screen, by pressing the Home button. The code for that is:
self.present(self.mealPlanViewController, animated: true, completion: nil)
The issue is that when they go back to that view controller, the Tab Bar is no longer there.
How can I present the view controller with the Tab Bar again?
You said:
"Later on the user can tap to get back to that screen, by pressing the Home button. The code for that is:"
self.present(self.mealPlanViewController, animated: true, completion: nil)
That is wrong. You are not "getting back" to anything. You are presenting self.mealPlanViewController modally. That means it is being drawn on top over everything else. If self.mealPlanViewController was previously on-screen, bad things may happen.
In order to help you sort it out, you need to explain your view controller hierarchy and the flow between screens. What is the name of your tab bar controller's class? Of the view controllers that are displayed as tabs? How are you getting from the tab bar controller to the screen that contains the home button?
I was able to figure it out - please see below:
I added #IBAction func unwindToContainerVC(segue: UIStoryboardSegue) {} to the view controller I wanted to go back to. This added an Action Segue.
I added the segue to the storyboard and gave it the identifier, "unwindToMealPlanViewController"
I added the following code to my Done button: self.performSegue(withIdentifier: "unwindToMealPlanViewController", sender: nil)
These were some very helpful resources in solving this:
How to perform Unwind segue programmatically?
https://www.hackingwithswift.com/example-code/uikit/how-to-perform-a-segue-programmatically-using-performsegue
https://www.youtube.com/watch?v=ULd2v4mHyQ4
I have a basic scenario:
I present a VC modally using self.present(, animated:, completion:).
Sometimes due to interactions in this modal VC i need to close one modal and open another one.
So i do the following:
weak var presenter = self.presentingViewController
let newVc = UIViewController()
presenter?.dismiss(animated: true, completion: {
presenter?.present(newVc, animated: true, completion: nil)
})
This works but there is the annoying delay when switching the VC's when user sees the original presenter and can try to interact with it (to open other modals...).
I tried setting animated: false but that doesn't seem to work :/
I can't really switch to UINavigationController model for this because the modals i am presenting themselves are Page View Controllers and have the whole hierarchy of dependent views; the user is never going 'back'; so i'd really like to just present the new modal as quickly as possible...
Update My question is not about how to control or choose the animation. My questions is about having no delay between the modals.
The built-in view controller architecture that switches views with no transition is the tab bar controller. So just turn your view controller into a tab bar controller — with no visible tab bar! To change to the other view controller just change tabs (in code). The change is instant.
This screencast makes it clear that this works as described. We present a view controller (yellow). Then we switch back and forth between two view controllers (green and yellow) as the presented view controllers, instantly. Finally, we dismiss whichever one (green or yellow) is showing. I'm doing it all with simple buttons but that's just for the demo; obviously you could do this however you like. It's the architecture that's the important thing.
I can think only of solutions which would require you to handle the animations yourself
create custom modal transition using UIViewControllerTransitionCoordinator
add your controllers to container views as suggested by #muhammed-gül
present newVC over self and dismiss all presented controllers when you're done
And just a tip, you don't always need to wait for the dismiss completion closure, you can call this and it usually works, but still the underlying viewController is visible.
dismiss(animated: true)
present(newVC, animated: true)
I've had this issue for months with multiple views, both Apple provided like ImagePicker and VCs from storyboard.
I believe that it has something to do with the underlying views we have both a tab bar controller and navigation controller in most views.
Strange thing is using some open source views from pods does not cause this bug.
So I'm two views deep on a navigation controller and present another view modally on top with present(vc, animated: true, completion: {})
Works like a charm, now dismissing that view with dismiss(animated: true, completion: nil) throws me back all the way to the initial view or root view of the navigation controller, had both happen before, depending on the presented view.
Update:
Build a sample project trying to reproduce the behavior but failed. Drew a reduced diagram to better explain the current bug behavior.
Also noticed that if I'm invoking the post view one step earlier in the Fandom view it works as expected.
In my case i am using UITabBarController, and I wrote code in viewWillAppear of UITabBarController
self.selectedIndex = 2
so when i present any thing from any controller whose parent is UITabBarController and when i dismiss that it automatically open third tab of UITabBarController.
Maybe you explicitly wrote any code to select specific index of TabBar.
Maybe this is useful for you or anyone else.
I am working on some old code that I didn't write, and it's really not architected well...
The situation is that a view controller presents a custom view controller modally, however every 30 seconds the presenting view controller is recreated.
The issue here is that if the modal is on screen when this happens, then any effort to dismiss it results in odd behaviour (such as a white screen).
I have tried calling [self.presentedViewController dismissViewControllerAnimated]; on the newly recreated controller, but presentedViewController is nil as you would expect.
I have also tried keeping a weak reference to the modal view controller, then when the presenting VC is reloaded, setting this value to that of the old VC. This has allowed me to call self.customModalVC dismissViewControllerAnimated]; but this is causing the aforementioned white screen, perhaps because it's presenting VC is no longer in the stack?
Any and all suggestions appreciated.
Try passing the navigation controller to newly presented ViewController:
presentedVC.navigation = self.navigationController
Add this to newly created one for dismissing
self.dismiss(animated: false) {
_ = self.navigation?.popViewController(animated: true)
}
I have a UIImagePickerController presented on screen. When I choose the photo I need to dismiss the PickerController then immediately present another PhotoEditController. Here is my code
picker.dismissViewControllerAnimated(false, completion: {
self.presentViewController(editPhotoVC, animated: false, completion: nil)
})
There is a 0.1s flash between dismissing old VC and presenting new VC so the presentingViewController (self) is shown. How do I avoid that in an elegant solution not hacking it through? Thanks
The standard way of implementation is to dismiss first view controller (VC) with animation and present second VC with animation.
However, depending on your view hierarchy, you could also have your second VC loaded first and then present first VC on top of it. With this simply dismissing first VC without animation should show underneath second VC without delay.
Third approach, as LLooggaann suggested, don't dismiss first VC and simply present second VC. Once done, dismiss the entire view controller hierarchy in one shot.
You could instantiate PhotoEditController's instance, then do something like this:
instance.willMoveToParentViewController(controllerThatPresentsPicker)
controllerThatPresentsPicker.addChildViewController(instance)
controllerThatPresentsPicker.view.insertSubview(instance.view, belowSubview: picker.view)
instance.didMoveToParentViewController(controllerThatPresentsPicker)
It looks a bit like a hack though
That is most probably because dismiss is sent to the VC that presented the picker and it dismisses the picker modal vc and executes the completion handler after attempting to present the parent VC which is itself.
one solution would be creating your own transition class, you can basically copy paste the code from here to do that