How to dismiss View Controller from another - swift

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

Related

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

Dismissing 3 layers of modal view

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.

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

UINavigationController stack issues with push and pop

So I have four view controllers, A, B, C, D and they are shown in that order. Let's say that I am at view C and I want to go to view D, so I push view controller D into the navigation controller stack, however, what I want is that when D tries to go back it goes directly to view controller B instead of C.
Is there a particular way to achieve this? The logical way is to do popViewController and then pushViewController: C. I tried this and it didn't work out.
Instead of pushing D, call -setViewControllers:animated: on your navigation controller with an array of view controllers ordered A, B, and D.
So the order is A->B->C->D right? And from D, when the "Back" button is tapped you want to go to B?
That means popping D and then popping C so you get back to B. That's what popToViewController:animated: does.
UINavigationController Class Reference
popToViewController:animated:
Pops view controllers until the specified view controller is at the
top of the navigation stack.
Note that this is different from popToRootViewControllerAnimated:
You can use popToRootViewControllerAnimated: if A is at the bottom of the stack or popToViewControllerAnimated: if it's somewhere in the middle.