iOS Programming: Understand UINavigationController and the RootViewController - objective-c

I'm reading Apple doc but I don't understand what a root controller is. The doc says that this is the controller at the bottom of the stack. Maybe, is this the controller that I've push the first time? Anyone can explain this concept?
From Apple doc UINavigationController, I've seen that there is a method called initWithRootViewController
Initializes and returns a newly
created navigation controller.
- (id)initWithRootViewController:(UIViewController
*)rootViewController
What does it mean? Then, is it possible to change the root view controller or not?

Remember that rootViewController isn't a type of object, but a property name. It just happens to be the particular UIViewController instance at the bottom of the stack, as Nubbel said.
Methods like initWithRootViewController are for situations when you want to initialize UINavigationController, and also tell it to "use this VC as the first VC in the heirarchy".
It seems like UINavigationController really doesn't want you to change that property after it's been created, but it looks like ppl have found ways to do it: - Remember to check the comments to see if this is an "approved" method of working.

Just like the Docs and you already said, it is the controller at the bottom of the stack, the first controller pushed to the stack. Subsequent controllers will be pushed on top of the RootViewController. To return to the RootViewController you can use the popToRootViewControllerAnimated:method.
Furthermore, you can't change the RootViewController!

Related

When will viewWill/DidAppear/Disappear is called anyway and how exactly does it work?

I understand that viewWillAppear will be called when duh.... when the the view is about to appear.
But how does IOS know that a controller's view is about to appear?
When exactly that and how it is implemented?
For example, does the childController.view check first that window is one of it's super ancestors? Does the view has a pointer to it's controller? How exactly that works? Does everytime a view is added it check whether it's window is it's super ancestor and whether it is the view outlet of a UIViewController?
For example, if I add childcontroller.view but not to a subview of any view that's being called. Will viewWillAppear called?
Does the childController need to be the a child of a parentController so that viewWillAppear of the childController will be called when the parentController's viewWillAppear is called automatically?
The view is loaded by your controller using the - (void)loadView method. This method is implemented to load a blank view or a view from a nib/storyboard. You only need to override it if you really need to create a view hierarchy from scratch.
All of the magic happens when the value of the view property is first requested and the controller detects the value is nil. All of the life cycle method calls are handled by the UIViewController. There is nothing you need to do other than implement the methods if you need them. Remember one thing: There is no guarantee the view has been loaded until the - (void)viewDidLoad method has been called.
Everything I've learned about controllers how they work has come from the View Controller Programming Guide.

How to Refer to the Current View Controller in the Code

I'm a new iOS developer with a simple question: I want to programmatically move from one view controller to the next, how do I write this code?
So far I have:
UINavigationController *navigationController;
navigationController = [[UINavigationController alloc] init];
[self.view addSubview:navigationController.view];
[navigationController pushViewController:viewController animated:NO];
I'm not even sure if this will work, ultimately, but my main question is "viewController" in line 4. The program doesn't know what that is. It is the name of my current view controller, but how do I set it up so that it knows what I mean by viewController?
As an aside, the above is part of an if/else statement that occurs and is connected to the NSUserDefaults class to make it such that the view controller I am referring to only loads if terms and conditions have not previously been accepted. Will that work? Thanks.
First the simple answer: pass self when you want to pass the "current" object.
The more important consideration is: is that nav controller on screen? It's likely that your code won't do much unless you use a navigation controller which is (probably) the window's root view controller.
This is fairly easy to setup in your storyboard ("embed in"->"navigation controller") and then you don't need to instantiate it in code, you simply use self.navigationController (usually).
Normally, you would instantiate viewController just before you push it, so the program will know what it is. And, sure, you can have an if statement, and push this new view controller based on how the if statement evaluates.
I'm not sure about the code you wrote -- whether that's right depends on the structure of your app, and where you're doing this. Often, the navigation controller is made the root view controller of the window, and you set the navigation controller with a root view controller of its own when you create it

addSubview: re-writes the navigation stack?

I'm working on a app which uses the controller containment pattern as described in the View Controller documentation in the iOS SDK.
I've written the controller container, and it works great. My controller is basically containing two sub views and it displays them both at the same time, sliding one over the other depending on what the user is doing. Works wonderful.
Now, I want to use this container controller in a navigation view. This is to get push segues to work. In effect, from my contained controllers, I want to be able to use the navigation stack, push a new controller on, and pop when the user is done.
However, I have noticed that if the navigation view is instantiated with my container controller as the root container, things fall apart.
In particular, I have noticed this:
In the iOS documentation, container controllers call addChildController: and then addSubview:. This seems to break the navigation stack, as the push segue does not work - it behaves like modal. I believe it does this because addSubview resets the navigation stack.
I confirmed this by replacing addChildController and addSubview with [self.navigationController pushViewController...]. I confirmed it is a problem with addSubview because I can reproduce the issue when I omit the call to addChildController.
When I do this, the navigation stack works properly. But of course, my container controller does not, as only the "most recently pushed" controller is visible.
I'm doing this because in my contained controllers, I want to push a new controller onto the stack, and when the user is done, I want to "pop" the stack, without reloading the "previous controller".
Using a modal segue reloads the previous controller; using a push controller does not.
I cannot find any documentation on the behavior of addSubview and it's effect on the navigation stack.
Thank you in advance for any light you guys can shed!
I'm having a bit of trouble completely understanding what you are doing, but I think that what you want to do is exactly what I'm doing.
I have a UINavigationController that has as its rootView a container UIViewController. That controller adds children per the normal methods. One of those children views pushes other views that may get popped.
One of those pushed views COULD message the appDelegate and make itself the rootViewController if it wanted to. In general, as long as you keep a strong reference to a view controller, you can remove it from whoever 'owns' it, and muck around with the navigationControllers viewControllers array to your hearts content.

UIViewController within a UIViewController

So I have a viewControllerA, and I want to add another View managed by viewControllerB to it. There is only one UISlider activating a simple action in viewControllerB. It won't crash if I don't touch this UISlider, it will once I use UISlider. I am using ARC. I am using:
[self.view addSubView: viewControllerB.view];
to add viewControllerB to viewControllerA. Am I missing something? Thanks.
OK. It looks like a really simple situation. I just added one view controller and one action. Here is the demo project code on github: https://github.com/randomor/Demo
The reason why I want this to work is because I have another app that will create a view controller on the spot and add it to anther view. And I don't want to do it modally, because I don't want the new view controller to cover the whole screen. Thanks.
SOLUTION: So I'm now just using the latest ViewController containment API:
[self addChildViewController:viewControllerB];
It works! as long as I added this line, the event will be passed to its own controller and it stopped crashing.
i recommend you, to use the following code
in ViewControllerA.h
#import "ViewControllerB.h"
in ViewControllerA.m (where you want to push the new controller)
ViewControllerB *newController = [[ViewControllerB alloc]init];
[self presentModalViewController:newController animated:YES];
in ViewControllerB.m you will need
[self.presentingViewController dismissModalViewControllerAnimated:YES];
to make it vanish again.
concerning multiple controllers for one open screen (Apple ViewController Programming Guide):
Each custom view controller object you create is responsible for managing exactly
one screen’s worth of content. The one-to-one correspondence between a view controller
and a screen is a very important consideration in the design of your application.
You should not use multiple custom view controllers to manage different portions
of the same screen. Similarly, you should not use a single custom view controller
object to manage multiple screens worth of content.
You should try and avoid the practice of nesting UIViewControllers. While it is technically supported in iOS5, it is ill-advised, for many reasons, including the type of problem that you're having (you have a dangling pointer to a UIViewController, which is why you are crashing).
http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
Although this question is extremely vague, I imagine that you are not keeping a reference to View Controller B, and so when view B tries to interact with it, it causes EXC_BAD_ACCESS.
What's the object that is set as the target for the slider? If it's a EXC_BAD_ADDRESS, then you may not be retaining the target, most probably the view controller for the slider.

Get notified when a view controller is about to be popped in iOS4

This question has been asked before, but the answered ones I could find were from 2009 and don't suit my problem. Let me reiterate the issue.
I have a UINavigationController that spawns and pushes lots of different UIViewControllers onto its stack. One of those deals with some Core Data operations that need to be saved when that one particular VC get's popped off the stack. Don't focus on the Core Data part, it's about the popping.
How can I hook into the moment that the UIViewController is going to be popped off the stack?
I was hoping for a delegate method of some sort, but couldn't find it. The UINavigationControllerDelegate protocol is very sparse.
I then started thinking of using viewWillDisappear, but that one is also called if another view is pushed onto the stack, so it doesn't provide the right moment.
This answered question, from 2009, opts to look at the viewWillAppear of the view controller that we're 'popping to', but since that call doesn't have a reference to the VC that needs to do the checking, this is unsatisfactory and will introduce a level of dependency that is counter productive (the VC is used by several NCs).
Another answered question, also from 2009, opts to subclass UINavigationController and rewrite the popViewControllerAnimated: method. Or alternatively use the VC's dealloc. My gut tells me that can't be the way to go.
Finally there's one last recent question from march 2011, but no one cared to answer it.
That leaves me in my current unsatisfied state of mind. Is there anyone out there with a better solution to finding the moment your UIViewController is popped off a UINavigationController's stack?
Cheers,
EP.
viewWillDisappear is the appropriate delegate. You will need to add logic within this method if you want to determine if the current view is being popped or a new view is being pushed. That's been answered here - viewWillDisappear: Determine whether view controller is being popped or is showing a sub-view controller
I believe I'd go the other direction on this, and try to catch the polling from the individual viewControllers rather than the navigationController. To an individual viewController, getting popped looks like it's being deallocated, and that's totally hookable.
Subclass UIViewController, implement your notification in its -dealloc. Be sure to call [super dealloc].
Then have every view that you push into your navigation controller subclass your new custom view controller subclass. They can do whatever they do in their own viewDidUnload, and then call [super dealloc] (in this case super is your UIViewController subclass) to fire the notification.