My use case as follows:
VC1 - Root Controller
VC2 & VC3 are different view controllers
VC1 is delegate of VC2
VC1 presents VC2
Then as delegated, VC1 dismiss VC2
Right after dismiss VC2, VC1 presents VC3
In the last step my transition goes bad as after dismissVC2 and beforePresentVC3, VC1 is visible during transition
How can I fix this problem or is it even possible??
NOTE: I want VC1 in window herirarchy so that I can go to VC1 from VC3
I think you're getting into some trouble with no sense. You could easily have a hidden navigationController and create and stack every VC and then move around with
self.navigationController popToViewController: animated:
or
self.navigationController pushToViewController: animated:
then you could move from 1 to 3, or 3 to 1 with no problem. If you created all the VCs and are available to access from any of the 3 vcs
Related
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 NavigationController that the following VC's are embedded in: VC1 -> VC2 -> VC3 -> VC4 -> VC5. My problem is that when I segue from VC5 (after editing is completed), I send you back to VC3, but I want to programmatically throw VC4 and VC5 off the stack, i.e. when the user is sent back to VC3, I want "back" in the navagitionBar to take you to VC2 (and not VC5 where you really came from).
This comes up a lot in IOS, where you want to edit the model, then send them back to the tableView/Collection view, but since editing is done, you don't want the editing viewControllers in the navigation stack anymore as its too confusing of UX.
In the screenshot below, the VC on the top right is VC5: which is segued back to the PinViewController (VC3) via self.performSegueWithIdentifier("backToPins", sender: self)
How can I do this?
don't use segue to come back (pop).
you should use popToViewController and pass specific viewcontroller as argument to pop that viewcontroller.
for example if you want to go on 3rd view controller out of five then you can do something like below. you can just change index from viewcontroller array to go different view controller.
let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController];
self.navigationController!.popToViewController(viewControllers[viewControllers.count - 3], animated: true);
If you are using segue that means you add (push) new viewcontroller to navigation stack. in your example your stack after reaching 5th view is like,
VC1 - VC2 - VC3 - VC4 - VC5 (top of stack)
now if you performsegue to go back to VC3 then stack should be like this,
VC1 - VC2 - VC3 - VC4 - VC5 - VC3(top of stack)
and if you pop to VC3 then your stack is like,
VC1 - VC2 - VC3 (top of stack).
so pop viewcintrollers to go back don't use segue
hope this will help :)
The best way to do this is via an unwind segue.
In VC3 you define an appropriate unwind function:
#IBAction func unwind(segue:UIStoryboardSegue) {
if let sourceViewController = segue.sourceViewController as? VC5 {
let myNewData=sourceViewController.someProperty
self.someFunctionThatUpdatesScene()
}
Then in the VC5 scene you can create an unwind segue in one of two ways.
If you want it to be triggered directly from an object, such as a UIButton, you drag from the action in the inspector to the exit icon at the top of the scene and select unwind from the pop up.
If you want to trigger the unwind programatically then you drag from the view controller object in the explorer on the left to the exit icon and select unwind from the popup. You will now see an unwind segue in the explorer and you can give it an identifier like you would with any segue. You can use this identifier with performSegueWithIdentifier
The advantage of this approach is that you don't need to make any assumptions about the depth of UINavigationController stack and you don't need to implement a delegate/protocol to pass data back.
Apple has a very good Tech Note on Using Unwind Segues
I have a PresentingViewController (VC1) and a PresentedViewController (VC2).
VC1 --Segue:Present Modally:Over Current Context--> VC2
VC2 --RewindSegue --> VC1
The problem is that when I rewind from VC2 to VC1, it is not being "reloaded". It is nil.
However if I present VC 2 modally: Full Screen, the rewind triggers a reload of VC1 and all is dandy.
Why does a rewind FROM a VC presented "Modally:Over Current Context" not reload the VC you are rewinding to? How can I present over Current Context and also force a reload of VC1 when I rewind?
Any help would be much appreciated. Thank you.
Solved the problem by adding the function I wanted to update in VC1 in the unwinding action
#IBAction func unwindToDetailViewControllerVC1(segue: UIStoryboardSegue) {
println("Unwinding to DetailViewControllerVC1")
rememberWhatSegmentIsPressed()
}
Also remember to tick the 'Defines Context' in the presentingViewController.
See: https://stackoverflow.com/a/27487057/4051036
i am new to IOS .
My question is, i have some view-controllers as NavigationController,mainVC, VC1, VC2, VC3, CameraVC. In cameraVC i have a done button having action doneClicked. These all View-Controller are pushed in NavigationController. VC1 is presented, not pushed in nav-controller. doneClicked function implemented poptorootviewcontroller. when i click on done button it let me to VC1 but not the mainVC. Is there any way so i can pop all view controller to VC1 and after this automatically dismiss VC1 to mainVC.
Make your MainVC as root view controller and in the IBAction of done button use the code to pop to MainVC.
[self.navigationController popToRootViewControllerAnimated:YES];
Hope it helps.
To pop view controllers
[self.navigationController popToRootViewControllerAnimated:YES];
and to dismiss presented view controller
[self dismissViewControllerAnimated:NO completion:nil]
[self.navigationController setViewControllers:#[mainVC]];
I think this code will work on your situation. iOS Developer Library:
Replaces the view controllers currently managed by the navigation controller with the specified items.
- (void)setViewControllers:(NSArray *)viewControllers
animated:(BOOL)animated
source
you must do this after dissmiss present view controller. Use delegate
Now let's think your navigation stack is empty and your root is mainVC. you want to present VC1 that's ok just present it. But you should give a delegate to mainVC to what's gonna happen after dissmissing VC1. For example you present VC1 from mainVC. And you want to push VC2 after dissmiss VC1. That's ok just in mainVC has a delegate so in this method
[self.navigationController pushViewController:VC2];
Present views does not affect your navigation stack. It's not in your stack. so everytime you dismiss it from a controller you should give an delegate to that controller to what will happen after dissmissing.
Try it.
Hope it helps.
Here let me mention viewcontroller as vc.. Using navigation controller I'm moving from vc1 to vc2. and from vc2 to vc3 I'm moving programatically, using modal. And I'm returning back again programatically to vc2. But here my navigation bar back button is disappeared in vc2 and I'm not able to move back from vc2 to vc1. Navigation bar back button works before entering vc3, I mean I can move to and from between vc1 to vc2 as many times as I can, but if I once enter vc3... I can come back to vc2 and from there I cannot go back to vc1.
vc2 to vc3....
VC3 *vc3 = [self.storyboard instantiateViewControllerWithIdentifier:#"VC3"];
[self presentModalViewController:vc3 animated:YES];
vc3 to vc2.....
VC2 *vc2 = [self.storyboard instantiateViewControllerWithIdentifier:#"VC2"];
[self presentModalViewController:vc2 animated:YES];
From "vc3 to vc2" you need to come as dismissing view controller vc3.Replace your code of vc3 to vc2.....
[self dismissModalViewControllerAnimated:YES];
In your code you are again presenting vc2 on vc3 instead of dismissing v3.