I'm using a navigationController to push a new view onto the stack. Once I'm done with that view I pop it. I want the original or root view at the bottom of the stack which then becomes active to know when this has happened to it can call a method on itself
There are two options
1. implement -(void) viewWillAppear:animated: This is often a useful strategy for knowing that a view is about to become visible. If you are always doing the same thing when this happens, then this is quick and easy.
2. Send a NSNotification. This is useful when you want your underlying view to perform some action in response to a particular event happening elsewhere.
Use the viewWillAppear delegate. It is called right before showing the view.
Related
New to iOS, kindly bare if the question is very basic? When I press the tab button multiple times, it is not invoking viewWillAppear function? If I am wrong, then which function gets invoked, each time a tab button is pressed being on the same tabview?
You are correct, viewWillAppear is a little special, it is usually called automatically but in some cases including when you are adding a view controllers view manually (view addSubview:), and also when adding this as a view controller to a UITabViewController it doesn't get messaged.
This however is only for the root view, as you navigate (maybe with a navigation controller) back and forth, that root view's viewWillAppear will get triggered as some point.
In short, if you need to implement something in viewWillAppear in these cases, you should message it yourself when you know it's going to be presented. You can handle this case in your view controller, check out the following article about the matter:
http://www.touchthatfruit.com/viewwillappear-and-viewdidappear-not-being-ca
Good luck.
I'm trying to figure out what logic should go into the different UIViewController methods like viewDidLoad, viewDidAppear, viewWillAppear, ...
The structure of my app is that I have a root view controller that doesn't really have a view of its own, rather it has a tab view controller and loads other view controllers into it. But in the future, it may have to load other view controllers instead of the tab bar controller depending on app logic.
My main question is, what do people usually put into the viewDidLoad, .... methods.
Currently I:
viewDidLoad - setup the tab bar controller and set its view to the view controller's own view
viewDidAppear - check if user has stored login info
if not - present with login
if so, login and get app data for first tab
I'm trying to figure out now if my logic for setting up my tab bar controller should go into loadView rather than viewDidLoad.
Any help would be great. Small examples found on the web are great, but they don't go into detail on how larger apps should be structured.
You should not implement both -viewDidLoad and -loadView; they are for different purposes. If you load a NIB, you should implement -viewDidLoad to perform any functions that need to be done after loading the NIB. Wiring up the tabbar is appropriate there if you haven't already done it in the NIB.
-loadView should be implemented if you do not use a NIB, and should construct the view.
-viewWillAppear is called immediately before you come onscreen. This is a good place to set up notification observations, update your data based on model classes that have changed since you were last on screen, and otherwise get your act together before the user sees you. You should not perform any animations here. You're not on the screen; you can't animate. I see a lot of animation glitches due to this mistake. It kind of works, but it looks weird.
-viewDidAppear is called after you come onscreen. This is where you do any entry animations (sliding up a modal, for instance; not that you should do that very often, but I was just looking at some code that did).
-viewWillDisappear is called right before you go offscreen. This is where you can do any leaving animations (including unselecting tableview cells and the like).
-viewDidDisappar is called after you're offscreen (and the animations have finished). Tear down any observations here, free up memory if possible, go to sleep as best you can.
I touch on setting up and tearing down observations here. I go into that in more depth in View controllers and notifications.
viewDidLoad will be called once per lifetime of each UIViewController's view. You put stuff in there that needs to be set up and working before the user starts interacting with the view.
viewDidAppear is called whenever the view has appeared to the user. It could potentially be called more than once. An example would be the root screen of an app using a UINavigationController to push and pop a hierarchy of views. Put stuff in there that you'd want done every time. For example, you might want to hide the UINavigationBar of the root screen, but show it for all subscreens, so you'd do the hiding of the bar here.
Therefore, you'd put your logic for setting up your UITabBarController in viewDidLoad, since it only should be done once.
Regarding your app, is there a reason why you don't just make the UITabViewController be the controller loaded by your app delegate? It seems that you have a level of indirection in your app that you may or may not need. It's probably better to simplify it now, and refactor later if you need something more complex.
could anyone help me with the next question. I am working on an app with the navigation similar to Notes app has. The problem I faced i how to implement navigation within one level and keep root view as the previous screen. So the scenario I need to achieve:
1. Drill down from the top level list to the detailed view.
2. From this detailed view navigate to the detailed view of the next/previous item.
3. Back button should lead to the root view.
I've tried several approaches and no-one worked fine for me. The most right solution from my point of view is to create a middle controller between the root and detailed controllers to handle this next/prev redirects. But the main problem here is that switching from one detailed view to another the navigation pane stays the same, so it's not involved in the animation, whether Notes app work right in this case.
Could you point me how it' better to arrange controllers interaction in this case?
Thanks in advance. Sorry for quite big post, was trying to be as informative as possible.
Best regards,
Roman
I think the best implementation is this way:
Say you have a Note class where the data is stored.
You could have two view controllers, one with the list of notes and one with the details.
Both of these can be added to a navigation controller.
Then, when you want to switch to the next note, you retrieve that object somehow and assign it to your detail controller with a -setNote: call. That method will be responsible for changing what the outlets are displaying.
I've resolved this question - it's enough to pop current view and push new one, created for the next object in the list. The only think to remember is to save the reference to the navigation controller before the pop call, because after it self.navigationController becomes nil.
Yeah, it's misbehaving badly. Jongsma's reply is the best. In case you wanted to re-create controllers, I've found this to work:
[parent performSelectorOnMainThread:#selector(showMyItem:)
withObject:myNextItem
waitUntilDone:NO];
[self.navigationController popViewControllerAnimated:NO];
performSelectorOnMainThread delays execution of the method until the next run of the main loop, and by then the previous controller is removed already and navigation controller is in a stable state.
The root question is "how many UIViewControllers can you push on the navigation stack?" without causing memory warnings or earning a watchdog termination.
Suppose I have an application that is basically a database for three entities where each can have a relationship with some other entity, and the relationship is shown on a UIViewController. Users can follow those relationships and each one brings up a new controller - If the entities are A, B and C and A->B->C->B->C->A then each kind of view is on the stack twice. I understand how to push and pop, how to push back to a particular controller, and I think rather than just extend the navigation stack indefinitely it might be best to reuse a view controller in the navigation stack.
To do this, every time I wanted a FirstEntityViewController I could scan the navigation stack to find an object where [self isKindOfClass:[FirstEntityViewController class]]; and then call methods designed to rejig that view for what I currently want to see - just refreshing the data in the same way you do when reusing a UITableViewCell.
This is fine except for the effect it might have on the NavigationController. If I use UINavigationController:popToViewController:animated: I think it is going to discard everything above the view I'm popping to, including the view which the user expects to find on tapping "Back" in the navigation bar. So user taps a relationship, taps back and goes "huh?"
If I remove the matching controller from the navigation stack and then pop it on to the top of the stack the back behavior remains OK as long as the user doesn't go back as far as the instance of FirstEntityViewController that was moved or else again navigation will seem inconsistent.
Is the right solution to remove the controller from the stack, and somehow hold a place in the stack so that when the reused controller is popped it can be replaced back where it came from? Should I maintain my own list of view kind and data display so that when popping I can replace the view below the view about to pop to, staying one step ahead of back navigation?
Or is that just getting too complicated? Is there no need to even worry about this situation because the OS reuses much of the view controllers in the same way as UITableViewCells are reused, and there is no real memory or performance impact in having a 50-deep navigation stack?
ViewController instances remain in the UINavigationController's stack, but any view except for the top view may be unloaded at any time (the view controller is notified via the viewDidUnload message).
In other words, the views underneath the top view do not hang around and will eventually be unloaded in low-memory conditions, so there's no need for you to attempt to re-use your view controllers.
Last I checked you can't push a viewcontroller that's already on a navcontroller stack back onto it again. You'll have to create a fresh viewcontroller and push it onto the stack and each back button will pop that one off the stack. The best you can do is make a cache of viewcontrollers and dole them out as-needed -- as long as they're popped off the navcontroller stack. But it probably won't buy you much by way of memory savings.
UITableViews are a bit different in that there's only a relatively small number of cells in view at any given time and as soon as the cell goes offscreen it's removed and returned back into the pool. If you can guarantee that the maxdepth of your chain is fixed, then you can pull a similar windowing scheme. If not, you may have to stick with going deep and be vigilant about releasing memory as soon as you can.
So the the problem is that when someone touches the back button on the UINavigationControler, I would like to run some code to update the datasource.
The problem is that i cant seem to find the right delegate to do it. only these are available on the nav controller delegate, and i want the 'didfinishshowing' type method.
– navigationController:willShowViewController:animated: optional method
– navigationController:didShowViewController:animated: optional method
The next best place i thought was the nav bar but when i try that.
Terminating app due to uncaught
exception
'NSInternalInconsistencyException',
reason: 'Cannot manually set the
delegate on a UINavigationBar managed
by a controller
This makes sense retrospectively, as you don't want some hacker messing around with the internals of the nav controller and stopping it from working.
This must is a common problem, and i have missed something simple.
Just so we're clear: view A is the starting point. User taps something and you slide right to view B. User taps the back button and you're going from B back to A and you want to do something as a result of the 'back' action.
There are three ways to do it (and on neither do you have to go near the navigationController -- these apply to the underlying viewControllers themselves):
As dmercredi suggests override viewWillAppear on view controller A so when you're heading back to it, it refreshes itself. Problem is that viewWillAppear is also called when A is called the very first time. So you'll have to set some sort of flag to distinguish between the first viewWillAppear and any subsequent ones when returning from B.
Override viewWillDisappear on view controller B and do your refreshing there. This will only get called when B is about to go away. If there's something on B that goes one level deeper or brings up a modal dialog on top, viewWillDisappear is going to get called so again you'll have to distinguish between the coming and the going.
Decouple the various views and use the delegate pattern. View controller A sets itself as a delegate of B and when B updates something it invokes the delegate method, so A is notified of the change and can update whatever it needs to. You can invoke the delegate method any time the user makes a change inside B or override viewWillDisappear and just do it one time on the way out.
Add your refresh code to the viewWillAppear:(BOOL)animated method on the view controller that is about to be displayed. In your case, that is the view controller that's already on the navigation stack.