When pushing a viewcontroller using UINavigationController into the view:
What is necessary for it to trigger
viewDidAppear: and viewWillAppear: ?
What makes it fail to not trigger viewDidAppear: and viewWillAppear: ?
We are having a hard time relying on wether those methods gets triggered or not.
UINavigationController calls these methods directly on the controller that's being pushed when you call pushViewController:animated: Similarly, UITabBarController calls these methods directly when you switch tabs, and UIViewController calls them when you use presentModalViewController:animated:. They also get called when a view controller's view is added to a window. I've never seen these methods fail to get called in these specific contexts.
Now bear in mind that these methods only get called on the controller being pushed or presented in these specific contexts. These methods won't get called, for example, if you add your view controller's view as a subview of some view other than the UIWindow. Apple's documentation states that view controllers are intended only to be used with full screen views, which are typically presented using one of the methods described above. It's possible to ignore Apple's advice and associate a view controller with a subview of another view controller, but you'll have to delegate the viewWill/DidAppear/Disappear calls from the container view controller to the nested controller manually.
Check that you've got the function name exactly right, for example:
- (void)viewWillAppear:(BOOL)animated
For example, if you forget to declare the animated parameter, your function won't get called.
This may sound obvious, but I've made this mistake and since the original post doesn't have a code sample I thought I'd share!
Related
I made a shared class which all classes in the program can see it, and if I change any variable in this shared class and all classes can see this change. I use a navigation controller to navigate between view controllers, and I put some code in viewDidLoad function, but when I navigate to the view controller this code called only once. What should I do to make this code run every time I navigate to this view controller?
You can put code in viewWillAppear or viewDidAppear
These are events that you can use:
– viewWillAppear:
– viewDidAppear:
– viewWillDisappear:
– viewDidDisappear:
– viewWillLayoutSubviews
– viewDidLayoutSubviews
I know there are a lot of similar questions, but I can't find one that specifically addresses this.
Why is self.navigationController null when called in viewDidLoad, but correct when called from outside viewDidLoad?
This is my output from NSLog(#"%#",self.navigationController); The first is called in viewDidLoad, the second I add a button to the interface with a method that calls NSLog(#"%#",self.navigationController);:
NavApp[31524:11003] (null)
NavApp[31524:11003] <UINavigationController: 0x6e21190>
I know there must be some simple explanation for this behavior, I'm just curious as to what it is. Thanks!
A view controller's view is loaded when you first access the -view method/property on that controller. After the view has been loaded, the viewDidLoad method is called. This is pretty straight forward. You also have to remember that the view can be loaded/unloaded multiple times if memory warnings are received while the view is off-screen.
So viewDidLoad does not mean your view controller has been inserted into a navigation controller. In the process of pushing a view controller onto a navigation controller, its view will be accessed and loaded, but this will happen before the whole push is complete. So viewDidLoad is clearly getting called before the navigationController property has been updated.
You also have to consider that some other part of your code could be accessing the view controller's view before you even push the view controller onto the navigation controller.
So viewDidLoad is the wrong place to do what you are doing. You probably want to use a method like viewDidAppear: so you know that the view controller's view is part of the view hierarchy when it is invoked.
How to pop to rootview controll without using popToRootViewControll method because this method does not implement viewDidLoad method of RootViewController???
What do you mean by "because this method does not implement viewDidLoad method of RootViewController"?
The "viewDidLoad" is called only one time if you don't have memory warning. If you get an memory warning your viewDidLoad method is called again.
You need to implement your logic in "viewWillAppear".
And maybe you should read View Controller Programming Guide for iOS
UIViewController Class Reference here you find the viewWillAppear, and viewDidAppear methods
I think you need to take a different approach to solving this problem.
If you have code in the root view controller's viewDidLoad method that you want to run again, then that code shouldn't be in the viewDidLoad method in the first place. viewDidLoad only runs just after the view controller's view is loaded (usually just once in the lifetime of the view controller). You should probably put that code it in its own method in the view controller, so you can still call it from the viewDidLoad method.
To have the code run when you pop to the root view controller, you can call that new method from the root view controller's viewWillAppearAnimated method, which gets called each time the view controller's view is about to be displayed.
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];
Here's what I have:
A MainWindow.xib file configured with one UIViewController (subclassed to RootViewController). This nib gets loaded at application launch.
RootViewController has two ivars, a custom subclass of UIViewController and a UINavigationController. Both of these are loaded from nibs.
When the application launches both ivars are initialized from their respective nibs, and then the UIViewController.view is added as a subview of RootViewController.view.
Inside UIViewController's view I have a control that triggers an animated swap of UIViewController and UINavigationController. This is where the problem comes in. The swap animates, but the UINavigationController's views are not properly displayed. I get a Navigation Bar with no title, and nothing else.
The UINavigationController nib and underlying functionality have been tested in a stand alone project, but there was no RootViewController.
So, my question is, can I even do this? I've successfully swapped other view controllers like this, but never a UINavigationController. I've seen some documentation that leads me to believe this might be a fools errand, but I haven't convinced myself of that yet.
Solution (Kinda):
I found a solution (workaround? hack?), but it leads to some more questions. I nixed using a Nib for the UINavigationController. Instead, I loaded my UINavigationController's rootViewController from a Nib and then created the UINavigationController programmatically with initWithRootViewController:.
NavRootViewController *navRoot = [[NavRootViewController alloc] initWithNibName:#"NavRootViewController" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:navRoot];
[navRoot release];
This works as I expect. Which leads me to the conclusion that the rootViewController property of the UINavigationController wasn't being set properly when I loaded navigationController from a Nib. And the question is, why? Should it?
Also, when you see something like this happening in one case, but not another, it can be beneficial to either create a subclass and make your nib point at that subclass, or if you already have a subclass use that.
In the subclass, override all the various init:, initWithNibName:bundle:, viewDidLoad:, viewWillAppear:, viewDidAppear: and any other appropriate methods, and in those override, just NSLog("") something about which method it is (with param values perhaps) and call the super implementation.
This will give you an observable "track" of which methods are called in which order, and you can set a breakpoint to see where that call comes from.
This will give you enough information to find missing method calls, and then you can pursue the correct problem either here, or through filing a radar or ...
In some cases, viewDidLoad and viewDidAppear or awakeFromNib may need to be called each time you add the UINavigationController back into the stack of UIViewControllers. It seems that when the typical code executes out of your AppDelegate, that the Window, or something behind the scenes is doing something special for UINavigationController that presentModalViewController doesn't do.
I think you may have missed a conceptual point.
A UINavigationController controls view controllers instead of views. It controls when and where view controllers themselves are loaded. The views themselves are loaded only as a side effect of the pushing and popping of their respective controllers.
Therefore, putting a navigation controller inside of a view controller seldom makes much sense.
If I understand what you are trying to do correctly, you should have the RootController actually set as the rootController property of the UINavigationController (yes the nomenclature is extremely confusing.) Then when your swap event occurs, you should have the navigation controller push the next view. The RootController view will disappear to replaced by the other. then you can repeat the process for an arbitrary number of view controllers.
Only in the case of a tabbar would you want a navigation controller to be a property of a view controller. Even then it should be at the top the tab's hierarchy.