'Reset' application to it's default state - ios

I have application with navigationController and some root viewController in it's stack. I need a way to reset application's state to default, i.e. when there is only navigationController and it's root viewController from any moment in my application. For example, I make some background data request to backend and get response, which says that I have to logout user immediately. In that moment, let's assume, user has opened a lot of other viewControllers in navigationController stack and maybe some presented viewControllers with their own navigationControllers. So what I need is a way to pull user back from wherever he is to the root viewController of application.
I'm aware of the fact that this is a bad user experience, but it's definitely better than crashing app, and anyway this should be a rare situation.
I don't think that creating a chain reaction between all viewControllers that will one-by-one dismiss them until it reaches my root viewController is a good idea for me. That's too much of identical code in every VC, and I don't want to create subclass of UIViewController and subclass all of my VCs from it to have this done.
Any suggestions?

You can use [self.navigationController popToRootViewControllerAnimated:YES]; to go to your main rootview controller from any view of your app.

Related

Is it alright to have the ViewController holder as a property of the root VC?

For all intents and purposes, MMDrawerController basically acts like a UINavigationController, where it holds a ViewController as the root VC.
Is it ok practice to hold the MMDrawerController in a variable in the root VC or should I store a reference to the MMDrawerController in the AppDelegate?
I don't want to run into any memory issues and the reason I ask is because at some point in a Registrations Controller I have to presentViewController(rootVC). This would be a different instance of the root VC and I would either have to store the reference to the MMDrawerController in the Registrations Controller OR maybe call upon the AppDelegate and change the MMDrawerController stored there to the new one for the new Root VC?
I suppose that second option would work fine, but still curious overall about my question in the title.
Also, one last thing: Is there any way when one calls presentViewController(...) to make the new ViewController the new initial controller? As it stands, in the new presented controller I would still be able to dismissViewControllerAnimated(...) which means that the underlying stack of View Controllers are still in memory. I'd like them dumped from memory. Should I even be concerned about that?

How do I jump to an arbitrary scene in a complex storyboard from a push notification?

Let's say I have a relatively complex storyboard with a login screen guarding access to a UITabBarController containing a couple of embedded UINavigationControllers and their children, like so:
The LoginViewController is the root VC, and decides if the user is logged in, either by checking stored credentials or asking for fresh ones. It then presents the UITabBarController as a modal.
There are four scenarios:
1) If the user starts the app from cold, they should see the ListViewController.
2) If the app is started from cold via a push notification regarding a "Foo", they should go directly to the FooDetailViewController.
3) If the app is warm-started via a push notification regarding a "Foo", they should go directly to the FooDetailViewController, no matter what VC they were looking at when the app was backgrounded.
4) If the user warm-starts the app, they should go back to whatever VC they were looking at when the app was backgrounded.
Where does the logic for all of this decision-making go? If I put it in the AppDelegate, I end up trying to push view controllers onto my hierarchy before they exist (Warning: Attempt to present ViewControllerX on ViewControllerY whose view is not in the window hierarchy!). The UITabBarController's viewWillAppear: lifecycle method does not seem to be being called on a warm start, and putting logic in every child view controller to check if it's been started from a push seems like coupling things that shouldn't be coupled (and in practice seems to lead to corrupted navigation stacks.)
How does everyone else structure this?
You must create that logic inside UIApplicationDelegate by creating properties needed to decide what is the view controller that has to be opened first.
The first place you have that guarantees the user interface (the window) is ready, is on the application delegate's -applicationDidBecomeActive:, but from here there are two courses of action:
For ListViewController and OtherListViewController is easy, you can just call -setSelectedViewController on UITabBarController;
For FooDetail and BarDetail, I think the best method is putting some logic on ListViewController, sending it a reference to the object you want to show the detail and by performing the relevant segue on ListViewController's -viewDidAppear:.

Skipping view controllers in a UINavigationController stack

Is it bad practice to skip view controllers in a navigation stack?
In my example I have a UINavigationController (which is being presented modally) with three UIViewControllers (firstViewController, secondViewController and thirdViewController), most of the time the user will be accessing firstViewController, maybe tapping a row which will then load secondViewController and so on, but what if in some circumstances I want to load the second or third view controllers immediately but still keep the first view controller in the stack, so that the user can still go back to the firstViewController.
I can do this but it doesn't feel right - is this something I should avoid doing?
Yes, it's called deep-linking, and it's perfectly fine. It's quite commonly done for things like coming from an external URL or push notification.

How to handle jumping back to previous screens in iOS

I am just looking for a sanity check here.
I have a screen that the user passes through on the way into the main application. That screen can be navigated back to from almost anywhere in the system.
As it stands I am just presenting ViewControllers without using a NavController to manage them (it does not seem applicable for most of my app, since screens are not necessarily sequential or related to one another).
My question is, if I have presented VC1, then navigate to other screens, and finally want to present VC1 again, I am doing something like:
[self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"VC1"] animated:YES completion:nil];
Is this bad form? Am I leaking memory by creating a bunch of VC1 instances or is there some magic that uses the previously created one?
If it is bad form, how do I get back to the original VC1 to reuse it?
Thanks for any input.
I think you pegged it: It's not a great idea to have multiple instances of the same view controller in memory at the same time. Every time you instantiate a new view controller and present it modally, you'll consume more memory.
The most elegant solution is the iOS 6 unwind segue. But most of us would be unwilling to give up on iOS 5 support quite yet.
If you need to support iOS 5, you could contemplate using navigation controller, but hide the navigation bar if you don't like it in your user interface. Then replace modal segues with push segues and now you can do popToRootViewController whenever you want to return to the main view controller.

Proper way of unloading/reloading a View Controller

I am new to iOS and wondering how to properly implement view controller unloading/reloading.
Right now my app has a NavigationController with a MainMenuViewController (custom view controller) set up as root view controller. During the course of app lifetime, new ViewControllers are pushed/popped on the Navigation Controller. This works fine, the appropriate ViewControllers are initiated (from NIBs) the first time they are pushed to the stack.
However, I now want to unload one particular ViewController when it is popped, then reload it automatically when it is pushed again.
I have added a [self release] to that ViewControllers viewDidDisappear: and it unloads, but when i try to push the view again, i get a message sent to dealloc'ed instance error and crash. Therefore, my questions are:
Is that a proper way to unload a popped ViewController?
How to check if a given ViewController is loaded or not?
How to force a reload? With loadWithNib:, then push onto navigation stack?
Regards,
Peter
Welcome to iOS programming. Your crash is a memory management issue. It may take you a bit to get the hang of it but memory management gets way easier if you just follow one rule:
an object needs to release anything it retains (alloc is equivalent to retain)
In this case, your view controller is releasing itself and it definitely did not retain itself. Here's how the sequence works with a navigation controller:
The navigation controller is initialized with a root view controller (the first one on its stack). Lets call this firstViewController
A user action tells firstViewController to initialize secondViewController and push it onto the navigation controller. In most cases, firstViewController will release the instance of secondViewController after pushing it. At this point, firstVC is done with secondVC. The navigation controller is now retaining secondVC
User touches the back button on the navigation bar of secondVC. The navigation controller will pop secondVC from the stack and release it. As long as no other object is retaining it, secondVC will get be dealloc'ed.
Now user is back in firstVC. They can do the same user action which will init and push a new instance of secondVC.
Hope that helps a bit.
I'd also recommend you (re)read the Apple docs and look at the sample code referenced in the framework docs.
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html

Resources