I'm looking into the viewDidLoad and viewDidAppear methods to better understand what they both do and I came across an article which uses the example of a banking application to explain how these methods work:
Consider a banking application that shows your current balance. A user
can tap on a button, which presents a list of nearby ATMs in a modal
view controller. In order to get the list of nearby ATMs, the
application must make a core location and web service request.
In this situation, a programmer could get away with requesting the
list of nearby ATMs from the server in viewDidLoad. The view
controller is only presented once, both viewDidLoad and
viewWillAppear: will be called back to back and only once for that
particular view controller instance. The net effect of the code in
either of these two methods will be the same.
But this is a bad idea. Consider what would happen if you wanted to
move the ATM view controller into a tab bar controller. Now, the ATM
view controller – with its ATM fetching code in viewDidLoad only
fetches the list of ATMs once. So you are in Atlanta on Tuesday, open
up your application to look for an ATM, then check your balance. Then
you travel to New York on Wednesday, re-open the banking application,
and you only see ATMs in Atlanta. The view was already loaded, no need
to call viewDidLoad and now you’re looking at stale data.
Sadly, I still don't fully understand how/why both viewDidLoad and viewWillAppear will be called 'back to back', or what adding the ATM view controller to a tab bar controller means in terms of these methods.
viewDidLoad method will call only once a life time of viewController and that is when viewController object will first load in memory.
where as viewWillAppear method will call every time when a view will appear to screen or you can say will be topViewController...
Explanation:
Consider you have tab based app with two tabs. Tab1 associated with viewController1 and tab2 is associated with viewController2. Now when you will run your app and you will see tab one is selected and viewController1 is on view and you want to change to tab2, when you will tap on tab2 then tabVieController2's object will create and load to memory first time hence its viewDidLoad method will call, and soon after that it will appear to window and viewWillAppear will also get call.
Now if you you try changing tabs by click on them only viewWillAppear methods will get called for both, as they are in memory already.
It simple, viewDidLoad get called when the view is load in, either via NIB, storyboard or with the loadView method. The viewWillAppear: is called when the view is presented.
When a view is added to a tab bar it only gets load once, thus the viewDidLoad will only be called once. But if the user switch to an other tab and back to the same view the viewDidLoad will not be called. This is because the view is already loaded.
However the viewWillAppear: is called in both cases just before the view is shown. Thus this will be called when the user first opens the tab and when it switches back to that tab.
I think they are referring to the fact that the view is loaded every time the modal view controller appears (thus the data is constantly refreshed) but only once when it is part of tab bar (only loaded on app launch). Kind of a whacky example to explain the methods though.
You might want to read up on the view controller lifecycle to know when to implement what in which method:
Responding to Display-Related Notifications
View Loading and Unloading
Related
I am having a multiple UIViewController embedded in a UITabbarController.
These are connected by Relationship Segues.
The starting point, VC1 initially loads data and is then processed to a "detail" view.
Whenever I change tabs and go back to VC1, the application gets stuck, as viewDidLoad is not called.
Is there a way to trigger a function each time VC1 is segued to?
the application gets stuck, as viewDidLoad is not called
The application is not stuck. It is working perfectly. viewDidLoad is called when a view controller is created and loads its view. That only happens once in the life of the view controller. When you leave a view controller and come back to it, it is still there (i.e. it is not being created from scratch), so naturally viewDidLoad is not called.
If your goal is to hear about the fact that the tab bar controller is switching to VC1, give the tab bar controller a delegate and implement tabBarController(_:didSelect:) or similar.
Even better, configure things so that there is no need to do this. If there is common data that is accessed by both view controllers, architect things so that a view controller sends new data up to a data controller and the data controller broadcasts news of the change down to all view controllers that need to know this.
If I have multiple view controllers being presented and dismissed in any order, can I be sure that iOS calls viewWillAppear methods in the right order (i.e. order of appearance)?
I cannot find any specific information about this in the documentation.
I think this is all you need to know about viewWillAppear from the docs:
This method is called before the view controller's view is about to be added to a view hierarchy and before any animations are configured for showing the view. You can override this method to perform custom tasks associated with displaying the view. For example, you might use this method to change the orientation or style of the status bar to coordinate with the orientation or style of the view being presented. If you override this method, you must call super at some point in your implementation.
Only thing that comes to mind that might not be absolutely clear is that this callback is called on the presenting view controller when presented view controller is going to be dismissed (so presenting view controller is going to appear again).
Therefore if A is a root, A.viewWillAppear will be called before it will appear on the screen. Then, if A presents B, just before B becomes visible, B.viewWillAppear will be called. And when B is being dismissed, A.viewWillAppear will get called again, since its view will appear again.
viewWillAppear() is called the first time the view is displayed and it is also called when the view is displayed again, so it can be called multiple times during the life of the view controller object.
It’s called when the view is about to appear as a result of the user tapping the back button, closing dialog, or when the view controller’s tab is selected in a tab bar controller, or a variety of other reasons. Make sure to call super.viewWillAppear() at some point in the implementation
Summary of question
UINavigationControllerDelegate:didShowViewContoller makes it possible to get notified whenever any view controller has been displayed (as opposed to being loaded), provided its within the context of a navigation stack.
I want to know if such observation is possible for all view controllers if there isn't a navigation stack.
More background
I have an app where view controllers can suddenly appear based upon timers and local notifications firing, thus their appearance is effectively random.
If one VC triggers and gets displayed at the same time as another was in the process of getting displayed then there can be an issue (if you're experienced with iOS you'll be aware if one VC pushes another from within its viewDidLoad, rather than its viewDidAppear you will get an "attempting to present X on Y whose view is not in the window hierarchy" error)
How I solve this is I have a list of VCs to display and they get displayed by a view controller co-ordinator which implements UINavigationControllerDelegate's didShowViewContoller and doesn't display the new VC until didShowViewController has been invoked.
This works perfectly.
But now my problem is I want to do a similar thing for an app that isn't using a navigation controller, and thus I can't use UINavigationControllerDelegate:didShowViewController to observe globally when a view controller has been displayed. Does anybody know of another elegant mechanism for doing so?
I have an iPad app with a UISplitviewController set as the root view controller of UIWindow. The master view controller (i.e. the view controller of the left view) is the UISplitViewController's delegate with the delegate method shouldHideViewController returns YES, this means when the app first launches the left view will be hidden and can be shown (and hidden) by the user gesture, i want to show the master view when i navigate to detail pages and prevent the user from hiding it using the gesture, i've tried to call shouldHideViewController method on the delegate to let it returns NO the second time it got called but this time it has no effect, the master view keeps hidden in detail pages and can be shown with the user gesture.
any ideas to achieve showing of the master view with this scenario would be highly appreciated.
This may only be a partial answer because I'm not sure what you mean that you call shouldHideViewController method on the delegate. My understanding is that only the UISplitView should call this method. If you call it, then it will not effect the UISplitView, because it wasn't what made the call. In other words, the delegate method is used by UISplitView to get some information (and allow you a place to do additional things to other stuff) but it is not used as a way to tell the UISplitView what to perform.
Having said that, at least for the gesture activation/deactivation, I would think that in splitViewController:shouldHideViewController:inOrientation: you could do something like
[svc setPresentsWithGesture:NO];
I don't see any way to programmatically tell the UISplitViewController to display or hide the master view controller because the delegate only tells it if it should proceed in presenting or hiding the master when it is going to try and do that. It does seem like there should be a way to do this though.
I'm working on a problem where I have a ViewController which opens a popOver when a button is pressed and then in the popOver you can edit some settings which affect the content shown in the mother ViewController (for instance change the color of the View, change a map or a table there). This can be off course called whenever the popOver is dismissed, but it needs to be live up-to-date.
I tried using delegates, where I can pass over data or call a function but the function won't be started on a ViewController wchich isn't active, right?
I also tried NSNotifications but it didn't work either.
I found several questions like this on stackoverflow, but there is no real answer yet:
UIPopoverController button selecting method of another ViewController but not updating its view iOS
Refresh the view controller from popover
Refreshing a view from a popover
Refresh a WebView in a ViewController from another ViewController
how to refresh or reload a uiviewcontroller
Can somebody please explain a generall way with some code snippets how to get this fixed?
Delegation is a fine solution. The target view controller is presumably still visible underneath the popover (which shouldn't fill the screen). In this case when the target receives the delegate callback it can do any updates and refresh its views to update the UI without any limitation. In this situation both view controllers (the 'target' and the popover) would both be classed as 'active' as they are both visible on screen.