I’m having a scenario,
I had 3 controllers, lets assume A,B and C. When app launches I’m transitioning from A to C using segue. Later, in C by using a button I’m moving to B using segue again. So, now I’m in B when I click “Back” it is transitioning to C, but it has to move to A.
How is this possible, any hint/idea?
Put this code in B controller's back button.
for viewcontroller in self.navigationController!.viewControllers as Array {
if viewcontroller.isKindOfClass(HomeVC) { // change HomeVC to your viewcontroller in which you want to back.
self.navigationController?.popToViewController(viewcontroller as! UIViewController, animated: true)
break
}
}
OR
If Class A is your RootView controller
self.navigationController?.popToRootViewControllerAnimated(true)
If your Class A viewController is root, you can set this code on back button:
self.navigationController?.popToRootViewControllerAnimated(true)
Related
I have a viewcontroller embedded in a navigationcontroller that pushes another viewcontroller onto the stack. This pushed viewcontroller has an embedded viewcontroller that segues/modally presents a final viewcontroller.
On a button click, I am trying to dismiss the final presented viewcontroller and pop the present-ing viewcontroller and return to the initial state.
Thus far, I've been able to get the dismiss going, but popping does not seem to work in the completion handler of the dismiss.
I've tried printing out the hierarchy, i.e. self.presentingViewController, self.navigationController, self.presentingViewController.presentingViewController..., all of which output nil, and am admittedly stuck now on returning to the initial state.
In looking at the view hierarchy, the final presented viewcontroller is beneath a UITransitionView separate from the rest of the stack I had mentioned earlier..
Any thoughts/guidance would be appreciated.
Since you mentioned segues I think unwind segues might help. I built a quick test project and they do indeed function correctly in your scenario.
There is a rather excellent answer in a related SO question What are Unwind segues for and how do you use them?. A summary of the answer for your particular case is: place the following function in your initial view controller:
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue)
{
}
You can then directly 'unwind' to that viewcontroller by using Storyboard Segues directly (as in the referenced answer) or programatically via:
self.performSegue(withIdentifier: "unwindToThisViewController", sender: self)
Again there's a good article entitled Working with Unwind Segues Programmatically in Swift which goes into lots of detail.
Can you try
if let nav = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
self.dismiss(animated:true) {
nav.popToRootViewController(animated:true)
}
}
I'm studying the tutorial from Apple Developer: Start Developing iOS Apps (Swift), and I'm confused with push and modal segue.
There are two scenarios, Save and Cancel button in navigation bar, backing to scene 1 from scene 2.
If the Cancel button is pressed, it will call different method for dismissing scene 2:
#IBAction func cancel(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
let isPresentingInAddMealMode = presentingViewController is UINavigationController
if isPresentingInAddMealMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController{
owningNavigationController.popViewController(animated: true)
}
else {
fatalError("The MealViewController is not inside a navigation controller.")
}
}
In this method, if the scene is presented by modal segue, dismiss(animated:completion:) is called, and
if the scene is presented by push segue, popViewController(animated:) is called for poping up the ViewController from the navigation stack.
But for the Save button, the tutorial overrides a method in scene 2, prepare(for:sender:), and a action method in scene 1, unwindToMealList(sender:).
And it drag the Save button to Exit (the button in the scene dock) and choose unWindToMealList(sender:) method.
So the flow will be: prepare(for:sender:) -> scene 2 dismissed and scene 1 presented -> unWindToMealList(sender:)
I'm wondering that the code snippets didn't dismiss explictly scene 2 and remove the ViewController in navigation stack when Save button is pressed.
I know that modal segue won't push ViewController to navigation stack, but push segue will push it.
Why the code snippets doesn't pop up it from navigation stack?
Many thanks.
It seems like the tutorial you are reading is making use of unwind segues.
Unwind segues, just like normal segues, have a source and a destination and you can prepare for it in prepareForSegue, but instead of presenting the destination VC, it will dismiss the source VC so that the destination VC is shown.
Unwind segues behave differently in different situations. When you present VC B from VC A using a push segue, and an unwind segue from B to A, the unwind segue will pop VC B from the navigation stack. When you present VC B from VC A modally, the unwind segue will dismiss the modally presented VC.
As you can see, unwind segues are quite smart. It will decide for itself what to do in order to show the destination VC. It can even pop two or more VCs in the navigation stack!
I have a view controller (in a navigation controller) A that segues to View Controller B, and View Controller C also segues to B (A -> B <- C). View Controller B is used to select a value that is passed back to either A or C, but if I click 'Cancel' or 'Done' it uses the unwind segue to go back to only one controller, whichever was connected last.
You can have an unwind segue return to whichever viewController initiated the segue. All you have to do is to implement the same method you are returning to in all of the viewControllers which segue to viewController B.
So in viewController A and viewController C, implement the following method:
#IBAction func backFromB(segue: UIStoryboardSegue) {
print("Back from B")
}
Then, when you Control-drag from your Cancel button in viewController B to the Exit icon at the top of the viewController, select backFromB from the pop-up.
Then when you run the app and hit Cancel in viewController B, you will return to either viewController A or viewController C (whichever one segued to B). This even works if one segue is a Show (Push) and the other is Modal.
Try and follow me through the image below here. ViewController A is pushing navController A modally, which in turn shows ViewController A through ViewController F as show segues. The reason for this is that I need a UINavigationBar in order to go back and forth between the different UIViewControllers. But at one point in this interaction line you can actually push up another navigation interaction line, from ViewController B to navController B. They will both eventually end up at ViewController F, but with slightly different data. When I reach ViewController F and I'm all done with that UIViewController, I'd like to return to ViewController A instantly, without seeing multiple dismiss animations.
To do this I've used wide range of different lines of code, but they all end up with the same issue. I can get them to dismiss fine, so I end up at ViewController A. But when I've used the interaction line using navController B it will animate the dismissal of ViewController F, but there will still be a ViewController F behind that dismissal, and when the animation is done it flickers away and I end up at ViewController A. Basically it looks like there is another ViewController F behind the one I'm dismissing, but there isn't (I've checked in the 3D View in Xcode).
I can't seem to fix it. I tried removing navController B and it works fine, I only see 1 animation and end up at ViewController A without the issue explained above, but I have to have that navController B there. Simply put there seems to be an issue when using several UINavigationControllers with modal views.
Code to dismiss: ViewControllerA.dismissViewControllerAnimated(true, completion: nil)
I would try to avoid presenting modal view on a modal view. Consider pushing View Controller G from the View Controller B to the same navigation flow, rather then presenting it modally. This would allow you to always dismiss only one navigation flow of the NavController A.
What I finally ended up doing was to check wether the PresentingViewController at ViewController F was ViewController A or not since if you went through ViewController G the PresentingViewController would be ViewController B. And in ViewController F I first dismissed the front modal presentation without animation and then the second one with animation. It looks okay.
if let _ = presentingViewController as? ViewControllerA {
view.window?.rootViewController?.dismissViewControllerAnimated(true, completion: nil)
} else {
dismissViewControllerAnimated(false, completion: nil)
view.window?.rootViewController?.dismissViewControllerAnimated(true, completion: nil)
}
The view.window?.rootViewController?.dismissViewControllerAnimated(true, completion: nil) will start dismissing all the modals form the very rootViewController. Just adding two dismissViewControllerAnimated() after each other in the else-clause will not work.
I have 3 view controllers: A, B and C. A is initial and it has 2 buttons: one is redirecting to B, and second one to C. When B is pushed, and button within B pressed user is redirected to C. Now on navigation bar within C, when I press back button I want to be redirected to A always, and never to B. Is that possible?
This is base functionality I want to achieve:
a)
A->B->C C back to A
b)
A->C C back to A
for (UIViewController *controller in self.navigationController.viewControllers)
{
if ([controller isKindOfClass:[A class]])
{
[self.navigationController popToViewController:controller
animated:YES];
break;
}
}
you can achieve this with presentview controller as modal view.first from a if first button is pressed present b view.if secnd button is pressed present c view and in c view controller put back button and add delegates and implement the delegate in a view controller.and you will have to dismiss one view after the other in a view controller.
The only way is replacing the back button by a custom button.
After reading what you said I think you meant implementing custom actions for each button.The way I would do it(In Swift) is this:
#IBAction func pressButton(sender : UIButton){
var SecondView :ViewController2
SecondView = self.storyboard?.instantiateViewControllerWithIdentifier("SecondView") as ViewController2
self.presentViewController(SecondView, animated: true, completion:nil)
self.viewWillDisappear(true)
}
This method I have used a lot and I know there is another way using segues but you still use #IBActions.You just create a new segue(identifier) and in the prepareforSegue method you say what happens when that segue is triggered