Presenting stack of view controllers - ios

Is there a way to present two view controllers at once? My scenario is the following:
From my root view controller (View 0), I want to modally present a view (View 1), and when dismissing that view, a new view (View 2) should be beneath it.
The view hierarchy would look like so:
- View 1
- View 2
- View 0 (root)
I tried presenting it without animations in -viewDidAppear in View 1. This does not seem to be called until the presentation animation is finished, though. And presenting it before -viewDidAppear wouldn't work since the view is not yet in the window hierarchy.
Are there any acceptable ways of doing this?

Presenting a UIViewController while another UIViewController is presenting is not possible without stability issues. As a user experience concern, I think that approach would also not be the best. They should know what to expect when they dismiss a view controller.
If you aren't hard set on using presentViewController: and you have a UINavigationController, instances of that class have a method setViewControllers:animated: that allow you push two UIViewControllers onto a stack. The user would be able to see what they get when they go back (the back item).
Otherwise, you can present on VC, and immediately on success present the other. That's the safest option of doing what you want.

Related

Is it possible to segue from a modal view to a view that is not in the hierarchy on a Navigation Controller in swift?

On my main view in a NavigationController I have a button that brings up a new view modally to submit a post. I have a button to dismiss this view which brings the user back to the main view, however if the users proceeds to make a post, I want it to dismiss the view and proceed to a show the post in the Navigation controller.
The ideal effect would have the standard navigation controller's back button on the top that can bring the user back to the main view when they have finished looking at their post.
I have tried several different methods, but I tend to get an error stating "Warning: Attempt to present (The View I Want to Show) whose view is not in the window hierarchy!
Thanks for any info!
Yes, it's possible. This is the best way I've found to structure the app to allow this functionality.
RootViewController
UINavigationViewController
ContentViewController (your current "main" view controller)
You're basically going to create a new RootViewController that is going to contain your current UINavigationController as a child. I usually setup the window.rootViewController programmatically in the AppDelegate. I would also keep a global reference to it.
Have a look at Apple's documentation on this. Basically, the RootViewController code would look like this:
[self addChildViewController:navController];
navController.view.frame = self.view.bounds
[self.view addSubview:self.navController.view];
[navController didMoveToParentViewController:self];
Now, whenever you need to present a modal view controller, present it from the RootViewController, instead of the current/top view controller on the UINavigationBar. This allows you to manipulate the UINavigtaionController independently and you won't get the error you're seeing.
So present the modal view controller from the RootViewController, this covers the UINavigationController and its content (top view controller). Make any changes you need to the UINavigationController stack (push, pop, etc...) with no animation. When you're done dismiss the modal view controller (with animation) and it will show you're adjusted UINavigationController.
Hopefully, this all makes sense. It's kind of complex and hard to explain in text. :p
If you don't want to setup a RootViewController, you might want to try presenting the modal view controller from the UINavigationController, as that might have the same effect. From the view controller just do self.parentViewController.present.
The main thing you're trying to avoid is presenting a modal from a view controller and then removing that view controller (pop) before the modal is dismissed. The view controller that presents the view controller is responsible for dismissing it.
I finally got it all working (I'm still new to Swift) and the two comments on the question were very helpful. The most helpful of all was the top answer here's example on github.
Similar question

What happens with presenting viewController after it presents a presented viewController?

What happens with presenting viewController after it presents a presented viewController. Does its instance live forward? Is it destroyed when it is obscured by presented viewController views? If it is destroyed, how do I tell the system not to destroy it, but keep it intact?
I'm designing a game in witch the game is obscured with a presented viewController when some button is clicked. When the player returns to the game, I want it to restore its state exactly as it was before presenting a viewController
The presenting view controller keeps it's instance, and control is returned to it after the presented view controller is dismissed. In fact, you should have the presenting view controller dismiss the presented controller if possible.
Since the instance of UIViewControllers stay intact when presenting (or pushing) another view controller, it is best practice to implement the didReceiveMemoryWarning method in your UIViewControllers to release any memory that you can (clear caches, etc). Any data you clear out you may want to re-populate in the viewWillAppear method.
Other info from the View Controller Programming Guide for iOS :
Support for presenting view controllers is built in to the UIViewController class and is available to all view controller objects. You can present any view controller from any other view controller, although UIKit might reroute the request to a different view controller. Presenting a view controller creates a relationship between the original view controller, known as the presenting view controller, and the new view controller to be displayed, known as the presented view controller. This relationship forms part of the view controller hierarchy and remains in place until the presented view controller is dismissed.

What is the proper way to remove a UIViewController from the background memory when using instantiateViewControllerWithIdentifier?

I am presenting a view controller like this:
UINavigationController * PlacesNC = [storyboard instantiateViewControllerWithIdentifier:#"PlacesNavigationController"];
[self presentViewController:PlacesNC animated:YES completion:nil];
Once I am at the final view controller that is presented using the navigation view controller, how can I remove the initial view controller from memory?
Edit
In my application it is possible to present multiple UIViewControllers in this fashion. I will try to give a representation of what my stack might look like.
____ modal vc 5 * current view controller
____ modal vc 4
____ modal vc 3
____ modal vc 2
____ modal vc 1
____ root view controller
So if I have a stack like this, how can I remove vc 1,2,3 and maybe even 4 so that I can release the memory these other view controllers are using up? This may not be the correct to allow such a stack as this, but this is what I have now, and I just need a temporary fix as the iPhone 4 cannot handle the amount of memory used in a stack like this. So until I can come up with an alternate way to present my view controllers I just need to be able to remove some of them from the stack.
You cannot, and you don't want to. It is still in the view controller hierarchy, as I show in this diagram (DrillViewController is presented, but RootViewController is still there, "behind" it):
Presenting a view controller does not destroy the presented view controller, nor should it; if it did, you would not be able to dismiss the presented view controller and find the presenting view controller still sitting there.
Moreover, in iOS 8 it is perfectly possible to present a view controller and show the presented view controller's view in front of the presenting view controller's view, which remains visible behind it. That, for example, is how a UIAlertController presentation works. Clearly it would be a disaster if the presenting view controller ceased to exist in that situation.
Now, it may be that what you really mean is: My view controller has a property that uses a lot of memory. I don't need to hang on to that when my view controller is not frontmost. So I'd like to release it when I present another view controller. In that case, just manage the memory manually: set that property to nil on viewDidDisappear:, and recover its value (somehow) on viewWillAppear: when the presented view controller is dismissed.
One final suggestion: Perhaps the real problem is that a presented view controller was just the wrong type of hierarchical arrangement to start with. Perhaps what you really want to do is replace your original view controller in the view controller with the new view controller - because you are never coming back to it and don't need it any longer. That is perfectly possible, but of course you'll need to set up a different hierarchy architecture.

difference between presentViewController and UINavigationController?

Please tell the difference between presentViewController and UiNavigationController? Could I use presentViewController instead of UINavigationController to navigate different views in the app?
Whats the scenario to use either?
presentViewController offers a mechanism to display a so-called modal view controller; i.e., a view controller that will take full control of your UI by being superimposed on top of a presenting controller.
UINavigationController offers a much more flexible mechanism where you can push a new controller, and later pop it, so to go back to the previous one, in a ordered way. Imagine that controllers in a navigation controller will just build a sequence from left to right.
I think that presentViewController is most suitable for use with just one view controller being presented at a time. You can surely use it to stack more view controllers one on top of the other (and thus sort of "mimic" a poor-man's navigation controller), but my bet is you will quickly find something not working as you expected.
Specifically, an example of such limitation is the following: when you dismiss a modal view controller (in order to "close" it), all of your modally presented view controllers (from the same presenting controller) will also be dismissed at once. So you simply will not be able to implement a "go back"/navigation like functionality.
So, it depends on what you are trying to do.
A UINavigationController is a subclass of UIViewController that manages a stack of view controllers and adds a back button etc.
presentViewController is a method of the UIViewController class you use to present a modal view controller.
The UINavigationController maintains a navigation stack for you. You are then able to navigate through hierarchical content.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html
If you use UIViewControllers presentViewController method you are basically just replacing the view controller. no navigation stack is maintained for you.
UINavigationController is a class, presentViewController is an instance method of UIViewController (iOS 5 + ), of which UINavigationController is a subclass.
pushViewController is a comparable method to presentViewController. It is an instance method of UINavigationController, for iOS 2 +

How to determine which view controller is currently active/the one displaying a view?

In my app I am queueing some local notifications, when they fire I must present a modal view. The trouble is I have numerous view controllers any one of which could currently be active and thus the one that needs to present the modal view controller. How can I determine which one is currently in use?
I am setting a navigation controller as the windows root view controller, and this can push any number of other view controllers, some of them themselves may also be currently presenting another view controller modally.
This must work on iOS 4 and 5.
I have a lot of view controllers so would like to avoid putting code in each of them to each check if they are currently the top one.
You can look at the navigation controller's topViewController property to find out which controller is at the top of the stack. This will be the one whose view is displayed.
Since you may also be presenting a modal view controller, you'll probably be more interested in the visibleViewController property, which will give you the controller for the current view whether its presented modally or pushed onto the navigation stack.
Create a variable that stores a pointer to the ViewController that was most recently pushed. Every time you push a new ViewController, update this variable. Then you'll always know which one is on the top!

Resources