Dismissing 3 layers of modal view - iphone

In my application, I have three layers of modal view controllers.
1) So my rootViewController is a tabbar.
2) On applicationDidFinishLaunching I am presenting a viewController, say viewController A modally above tabbar.
3) On click of a button in 'viewController A', I present another viewController B modally.
4) And a button action on viewController B presents navigationController modally with its rootViewController as viewController C.
5) Finally from viewController C, I want to go back to viewController A.
I tried using
[[[self parentViewController] parentViewController]dismissModalViewControllerAnimated:YES];
in viewController C, but it reverts me back to viewController B instead of viewController A.
How can I revert back to viewController A.
Any help would be appreciated.

push all the 'modal' views on a navigationControl with a transitionStyle that looks like the modalTransitionStyle..dont reinvent stuff
present all in one modal navigationController Id say :)

You've got a complex VC stack. You could as one poster suggested implement a custom dismissView method, but that would be fragile: if you reuse this view, or move it in your app, it will cease functioning, because it relies too much on specific knowledge of how other VCs have configured their state.
You could configure a delegation chain. This would be the standard way to manipulate views: the presenting VC is also responsible for removing anything it presents.
To do so, build a protocol implemented by B, and initialize C with a reference to B. Similarly, initialize B with a reference to A (with potentially the same protocol, depending on any other communication that needs to be passed between them.)
Then when the button is clicked on C, it calls B's delegate method. B unwinds C as appropriate, and calls A's delegate method. A unwinds B as appropriate.
This has the advantage of keeping VC knowledge encapsulated: A knows how it presented B, so it knows how to unpresent it, and B knows how it presented C, and knows how to un-present it. In no case does one VC need to make assumptions about how it was presented by another.

Implement the view dismissing method like:
- (IBAction)dismissView
{
[self dismissModalViewControllerAnimated:NO]; // dismiss c
[[self parentViewController] dismissModalViewControllerAnimated:NO]; //dismiss b
}
[self parentViewController] will return the parent view of viewControllerC, that's viewControllerB. So it'll dismiss that view too.

Related

How to dismiss View Controller from another

So I have three ViewControllers: A, B and C. I have a listener from firebase in ViewController B which when activated, if you are currently in ViewController C, it should dismiss ViewController C. However, if you are in ViewController B, it should not dismiss back to ViewController A. The code I have so far is:
self.parentViewController?.dismissViewControllerAnimated(true, completion: nil)
which is in the listener of ViewController B. The problem I have with this is that if i'm in ViewController B, it dismisses it. What should I add to this code (like an if statement of some sort) to check that if the user is currently in ViewController C on his device, then dismiss that, otherwise, do nothing.
Maybe you can do something like this:
if(self.parentViewController?.isKindOfClass(ClassToDismiss){}

performSegueWithIdentifier and popViewControllerAnimated breaking screen navigation

I have 4 ViewControllers A, B, C, D. I move from A->B->C->D using Push Segues created from Storyboard.
Due to the logic of my app, if the user wants to go back from Screen D, screen C is no longer valid and I redirect the user to Screen C by using performSegueWithIdentifier
The problem starts now - I can't move back from Screen B->A using [self.navigationController popViewControllerAnimated:YES];
I would like to know why is this happening and how can I address such a scenario & fix the navigation?
What is the type of segue that you use?
Why don't you use:
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
You can access the stack of view controllers using the viewControllers property of UINavigationController.
Here is Apple's documentation on UINavigationController

iOS - strangeness in the navigationController

I have createa an app that is based on "Single View Application" Xcode template. It has a navigation controller and a rootViewController.
When I am on the rootViewController and I do
[self presentModalViewController:nextModalViewController animated:YES];
the new view controller is animated in.
My problem is this. I have presented a lot of viewControllers in sequence, that is
A > B > C > D
or in other words, I have presented B from A using presentModalViewController, C from B and so one. Yes, I have to use presentModalViewController because I have a special animation going on to transition between viewControllers and I cannot use [self.navigationController push...
My question is: what happens when I use presentModalViewController regarding to the navigation stack? Is the controller being presented pushed to some stack? is there a way to obtain references to all navigationControllers that were presented at a given time? something like that navigation stack? I mean, suppose I am on D and I want to get a list of all controllers presented before D.
I know I can create properties and pass that along. I am just wondering if theres something already built on iOS that does that.
thanks.
In iOS 5 and later, UIViewController has a presentingViewController property that returns the view controller that presents the receiver. In iOS 4 and earlier, use the parentViewController property of the presented view controller to access its presenting view controller. So you can access, for example, the C view controller from D by accessing these properties appropriately. See the docs for further information.
If you want to access all the view controllers as in a chain, you can do this:
UIViewController *node = self;
while (node != nil) {
// do something with the view controller, then skip to its parent
node = node.presentingViewController;
// or node = node.parentViewController; in iOS 4 or older
}
I'd subsequently check the size of the viewControllers property from your UINavigationController after every modal presentation, and check if it grows or not.
UIViewController *theControllerYouWant = [self.navigationController.viewControllers objectAtIndex:(theIndexOfYourViewController)];

Go back two levels with UINavigationController and Storyboard

I have a Storyboard with three views A, B and C (and more). From view B, the user can go back to view A by tapping the left button bar item that's automatically created. I've changed the label on this to "Cancel" by setting the Back Button property of A's navigation item.
C should have the same Cancel button, but it doesn't make sense to go back to view B; rather it should jump back to A. I know how to do this programatically but where do I put the code so it's triggered when C's Cancel button is tapped?
I found this way simplest, just put index:
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
I think you cannot override the back button function. What I do in those cases, is create a left bar item and on it's function decide which navigationController view to send the user to.
I do that by using:
[self.navigationController viewControllers] objectAtIndex:1];
Index 1 will be the first view after the root viewController, if A is your rootView, you can also use:
[self.navigationController popToRootViewControllerAnimated:YES];
If you want to cancel an action from B or C and go back to A it may make more sense to present the view controllers modally.
From the iOS human interface guidelines:
A modal view generally displays a button that completes the task and dismisses the view, and a Cancel button users can tap to abandon the task.
Use a modal view when you need to offer the ability to accomplish a self-contained task related to your application’s primary function. A modal view is especially appropriate for a multistep subtask that requires UI elements that don’t belong in the main application user interface all the time.
Instead of pushing C, use the replace option in the segue so that it will do this automatically for you.
Another option: if you put the following into B, it will remove it from the stack when C is presented. Then the stack looks like [A,C] so back will go straight to A.
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// remove this view controller from the stack
if let nav = self.navigationController {
let vcs = nav.viewControllers.filter {(vc) in
return (vc as? MyViewController) == nil
}
self.navigationController?.setViewControllers(vcs, animated: false)
}
}

viewWillAppear only being called once

Here's the scenario, switchViewController is the view added to the main window. So switchViewController is the main view, so if I want to go view B, I will addsubview of view B, there isn't a need to remove switchViewController's view right?
The issue is after I go back from view B to switchViewController's view, the method viewWillAppear is not being called anymore.
Why is it so?
viewWillAppear: is not called automatically when a view is removed from or added to the view hierarchy. It is the responsibility of the view controller to call it at the right time. The built-in view controller classes do this whenever you present or push a new view controller. Since you do not use this mechanism in your app, the method doesn't get called (unless you call it yourself).
That's because it never disappeared, you were just putting something else in front of it. If you want to navigate from one screen to another and back, they should be separate view controllers, and you should be using UINavigationController and its pushViewController:isAnimated: method.
It's not getting called beause your just modifying the first view, not navigating to a different one.
You might consider embedding your view in a Navigation controller, then calling your ViewB with
[navigationController pushViewController:viewB animated:YES];