I have seen a lot of posts on stack overflow stating that the viewDidLoad method of controllers is only called the first time the controller is accessed and not necessarily every time but always at least once.
This is not what I am seeing at all! I put together a simple test to highlight this:
https://github.com/imuz/ViewDidLoadTest
It seems for navigation controller segues and modal views viewDidLoad is always called. The only time it is not called is when switching between tabs.
Every explanation of viewDidLoad I can find contradicts this:
When is viewDidLoad called?
UIViewController viewDidLoad vs. viewWillAppear: What is the proper division of labor?
http://www.manning-sandbox.com/thread.jspa?threadID=41506
And apples own documentation indicate that a view is only unloaded when memory is low.
I am currently doing initialisation in viewDidLoad making the assumption that it is called with every segue transition.
Am I missing something here?
Phillip Mills' answer is correct. This is just an enhancement of it.
The system is working as documented.
You are seeing viewDidLoad because the view controller being pushed onto the navigation controller is a new instance. It must call viewDidLoad.
If you investigate a little further, you would see that each of those view controllers are deallocated when they are popped (just put a breakpoint or NSLog in dealloc). This deallocation has nothing to do with the view controller container... it does not control the life of the controller it uses... it is just holding a strong reference to it.
When the controller is popped off the navigation controller stack, the nav controller releases its reference, and since there are no other references to it, the view controller will dealloc.
The navigation controller only holds strong references to view controllers that are in its active stack.
If you want to reuse the same controller, you are responsible for reusing it. When you use storyboard segues, you relinquish that control (to a large extent).
Let's say you have a push segue to view controller Foo as the result of tapping some button. When that button is tapped, "the system" will create an instance of Foo (the destination view controller), and then perform the segue. The controller container now holds the only strong reference to that view controller. Once it's done with it, the VC will dealloc.
Since it creates a new controller each time, viewDidLoad will be called each time that controller is presented.
Now, if you want to change this behavior, and cache the view controller for later reuse, you have to do that specifically. If you don't use storyboard segues, it's easy since you are actually pushing/popping the VC to the nav controller.
If, however, you use storyboard segues, it's a bit more trouble.
There are a number of ways to do it, but all require some form of hacking. The storyboard itself is in charge of instantiating new view controllers. One way is to override instantiateViewControllerWithIdentifier. That is the method that gets called when a segue needs to create a view controller. It's called even for controllers that you don't give an identifier to (the system provides a made-up unique identifier if you don't assign one).
Note, I hope this is mostly for educational purposes. I'm certainly not suggesting this as the best way to resolve your problems, whatever they may be.
Something like...
#interface MyStoryboard : UIStoryboard
#property BOOL shouldUseCache;
- (void)evict:(NSString*)identifier;
- (void)purge;
#end
#implementation MyStoryboard
- (NSMutableDictionary*)cache {
static char const kCacheKey[1];
NSMutableDictionary *cache = objc_getAssociatedObject(self, kCacheKey);
if (nil == cache) {
cache = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, kCacheKey, cache, OBJC_ASSOCIATION_RETAIN);
}
return cache;
}
- (void)evict:(NSString *)identifier {
[[self cache] removeObjectForKey:identifier];
}
- (void)purge {
[[self cache] removeAllObjects];
}
- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier {
if (!self.shouldUseCache) {
return [super instantiateViewControllerWithIdentifier:identifier];
}
NSMutableDictionary *cache = [self cache];
id result = [cache objectForKey:identifier];
if (result) return result;
result = [super instantiateViewControllerWithIdentifier:identifier];
[cache setObject:result forKey:identifier];
return result;
}
#end
Now, you have to use this storyboard. Unfortunately, while UIApplication holds onto the main storyboard, it does not expose an API to get it. However, each view controller has a method, storyboard to get the storyboard it was created from.
If you are loading your own storyboards, then just instantiate MyStoryboard. If you are using the default storyboard, then you need to force the system to use your special one. Again, there are lots of ways to do this. One simple way is to override the storyboard accessor method in the view controller.
You can make MyStoryboard be a proxy class that forwards everything to UIStoryboard, or you can isa-swizzle the main storyboard, or you can just have your local controller return one from its storyboard method.
Now, remember, there is a problem here. What if you push the same view controller on the stack more than once? With a cache, the exact same view controller object will be used multiple times. Is that really what you want?
If not, then you now need to manage interaction with the controller containers themselves so they can check to see if this controller is already known by them, in which case a new instance is necessary.
So, there is a way to get cached controllers while using default storyboard segues (actually there are quite a few ways)... but that is not necessarily a good thing, and certainly not what you get by default.
I believe the Apple documentation is describing a situation where the view controller is not being deallocated. If you use a segue, then you are causing the instantiation of a new destination controller and, being a new object, it needs to load a view.
In xib-based apps, I have sometimes cached a controller object that I knew I might re-use frequently. In those cases, they behaved in keeping with the documentation in terms of when a view had to be loaded.
Edit:
On reading the links you included, I don't see any contradiction in them. They, too, are talking about things that happen during the lifespan of a view controller object.
It is called every time the controller's view is loaded from scratch (i.e. requested but not yet available). If you deallocate the controller and the view goes along with it, then it will be called again the next time you instantiate the controller (for example when you create the controller to push it modally or via segue). View controllers in tabs are not deallocated because the tab controller keeps them around.
This has been long on my mind, and I do not really know how to properly add a view that's managed by a view controller to another view controller's view.
This does not work, because the view does not finish loading
self.messageViewController = [[PopupMessagesViewController alloc] initWithNibName:#"PopupMessagesViewController" bundle:nil];
[self.view addSubview:self.messageViewController.view];
How can I add a UIView that a view controller creates from a nib to another view controller's view? How can I force such view to load before adding it?
You need to create a Container View Controller. While iOS 5 explicitly supports container controllers, you can create container controllers in previous versions. All iOS 5 does is do some automatic forwarding of rotation/appearance events (optional...and personally I find them annoying, sending the events before I'm ready) and give you some extra methods to use in your implementation. The real issue in creating a Container View Controller is sending all the appropriate events to the sub-controllers and making sure you manage your controllers in a way that is consistent with Apple's implementation. Otherwise, you'll get odd behavior in your sub-controllers. You really need to make sure you fully understand how view controllers work in their entirety before you do this. I recommend reading the following:
Here's some links to info: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html -Scroll down to: Implementing a Container View Controller
Also here for the view controller life cycle, which will help you figure out which calls need to be made in which order: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1
I do recommend reading the entire View Controller Programming Guide....you can gleam a lot of information from there: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007457-CH1-SW1
In general, don't do that. You're breaking some of the assumptions about how UIViewControllers will be used and it is likely to cause you problems in the future. You're not going to be able to count on the subview's controller receiving all of the UIViewController lifecycle method calls you might expect.
Valid solutions are to use the iOS 5 container view controller methods to add the subview's controller as a child view controller or to have a non-UIViewController controller class responsible for managing that subview if you need to encapsulate that behavior.
Try this
- (void)viewWillAppear: (BOOL)animated {
[super viewWillAppear: animated];
[self.messageViewController viewWillAppear];
}
- (void)viewDidAppear: (BOOL)animated {
[super viewDidAppear: animated];
[self.messageViewController viewDidAppear];
}
I have a tab bar controller with 4 tabs. Each tab has its own view controller and a UIWebView.
Let's say I have a button (button1) in vc1 and an instance method onClick1 as well. In vc2 I have a method named reload. My question is, how do I access the specific instance method, onClick1 in vc2, from vc1?
For further detail, I'm actually trying to code a simple shopping utility for the iPhone. When a user adds an item to the cart from the browse view, I want to be able to automatically reload the cart view.
Below are some examples of what I mean. This problem has been more difficult than I thought. I'm not sure if I have redesign my application or what. Perhaps have both vc1 and vc2 belong to a subclass of vcmain and have reference to each of them there? However, if I do that, then how do I refer them to their corresponding .xib? Thanks guys!
#implementation viewController 1
//Reloads vc2
-(IBAction) onClick1: (id) sender {
//Calls vc2 reload
[vc2 reload];
}
#end
#implementation viewController 2
//Reload View
-(void)reload {
[webView reload];
}
#end
You've basically got three approaches, each one of which has plusses and minuses. I'm going to give you a high-level overview and let you go to Apple's quite comprehensive documentation for details. Hopefully I can give you the right terms to google for more specific help.
The approach that #dredful quite ably details is to have a handle to the "other" view controller(s) and call methods on them directly. That works fine, but it can be confusing and cumbersome handing pointers to all your controllers around, and traversing the view hierarchy to get get at the controller you want can be very tricky indeed.
The second approach is Key-Value Observing. You can register one view controller to "watch" a particular key (named property) of another view controller, and fire a particular selector when various things happen with it. This is kind of magical and nice, although at some point you have to have pointers to both controllers at the same time, which doesn't entirely relieve the downside of the "call it directly" method above. It is also a sort of unfortunate coupling of view control and data, kind of breaks MVC.
The third approach is using NSNotificationCenter. A class can post a notification, and any other object that registers itself to listen for that sort of notification can be triggered when that happens. It's nice because you might have LOTS of different objects adding items to the cart, and they can just shoot the notification center a note (even passing it an object or arbitrary data, if it wants), and the cart view can consume those notifications, catch the passed objects, and do its thing, not caring in particular who's talking to it. It keeps separate pieces of your application nicely decoupled. The downside is, it's got a bit of overhead to it, and whatever selector the notification-consuming class performs happens synchronously, so you can't hide network activity or some other long process there.
I'm thinking you should already have something like a base UIViewController (let's call it MyTabBars) that has a UITabBarController *tabBarController containing all your Tab Bar View Controllers. If that sounds familiar, you will want a method in MyTabBars called -(void)reloadCart. reloadCart will walk the array of tabBarController.viewControllers. On each viewController you can perform a respondsToSelector:#selector(reload) and if the specific viewController qualifies then it calls that selector method.
In order to do this you probably want all your vc1, vc2, ... files to have an id delegate defined and synthesized. When MyTabBars creates the different tab bars it sets the vc1 and vc2 delegate to self.
#implementation MyTabBars
//Reload Cart View
-(void)reloadCart {
for (UIViewController *thisUIViewController in tabBarController.viewControllers){
if ([thisUIViewController respondsToSelector:#selector(reload)]) {
[thisUIViewController reload];
}
}
}
#end
Assuming you know how to pass a delegate of MyTabBars into your vc1 and vc2, then you can now have the following code in vc1:
#implementation viewController1
//Reloads vc2
-(IBAction) onClick1: (id) sender {
//Calls MyTabBars reloadCart which will look for all tab bar view controllers
//that have the 'reload' method
[delegate reloadCart];
}
#end
This solution idea will cause MyTabBars to trigger any reload method found in any of our tab bar view controllers. Therefore be careful with the naming of such a method in your vc1, vc2, etc files. This solution will trigger a unique vc method or multiple vcs with the same method depending on your naming convention.
Hope this helps.
I have a viewController (Planner) that loads two view controllers (InfoEditor and MonthlyPlan) when the application starts. MonthlyPlan is hidden behind InfoEditor (on load).
So my question is when I exchange InfoEditor for MonthlyPlan (MonthlyPlan gets brought to the top) how can I have data on the MonthlyPlan view be updated. An NSLog in viewDidLoad is being called when the application starts (which makes sense.) NSLogs in viewDidAppear and viewWillAppear aren't doing anything.
Any ideas?
Thanks!
-- Adding more details --
I'm creating the view hierarchy myself. A simple viewController that is just loading two other viewControllers. The two child viewControllers are loaded at the same time (on launch of application.) To exchange the two views I'm using this code:
[self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:0];
The exchanging of the views is fine. The part that is missing is just some way of telling the subview, you're in front, update some properties.
There's a lack of details here. How are you "exchanging" the two views?
If you were using a UINavigationController as the container then viewWillAppear/viewDidAppear would be called whenever you push/pop a new viewController. These calls are made by the UINavigationController itself. If you ARE using a UINavigationController then make sure you have the prototypes correct for these functions.
- (void)viewWillAppear:(BOOL)animated
If you are trying to implement a view hierarchy yourself then you may need to make these calls yourself as part of activating/deactivating the views. From the SDK page of viewWillAppear;
If the view belonging to a view
controller is added to a view
hierarchy directly, the view
controller will not receive this
message. If you insert or add a view
to the view hierarchy, and it has a
view controller, you should send the
associated view controller this
message directly.
Update:
With the new details the problem is clear: This is a situation where you must send the disappear/appear messages yourself as suggested by the SDK. These functions are not called automagically when views are directly inserted/removed/changed, they are used by higher-level code (such as UINavigationController) that provides hierarchy support.
If you think about your example of using exchangeSubView then nothing is disappearing, one view just happens to cover the other wholly or partially depending on their regions and opacity.
I would suggest that if you wish to swap views then you really do remove/add as needed, and manually send the viewWillAppear / viewWillDisappear notifications to their controllers.
E.g.
// your top level view controller
-(void) switchActiveView:(UIViewController*)controller animated:(BOOL)animated
{
UIController* removedController = nil;
// tell the current controller it'll disappear and remove it
if (currentController)
{
[currentController viewWillDisapear:animated];
[currentController.view removeFromSuperView];
removedController = currentController;
}
// tell the new controller it'll appear and add its view
if (controller)
{
[controller viewWillAppear:animated];
[self.view addSubView:controller.view];
currentController = [controller retain];
}
// now tell them they did disappear/appear
[removedController viewDidDisappear: animated];
[currentController viewDidAppear: animated];
[removedController release];
}
I would just add an updataData method to each subview and call it at the same time you bring it to the front. You would need to add a variable to your root view controller to track the active subView:
[self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:0];
if (subView1IsActive) [subView1Controller updateData];
else [subView2Controller updateData];
I've read numerous posts about people having problems with viewWillAppear when you do not create your view hierarchy just right. My problem is I can't figure out what that means.
If I create a RootViewController and call addSubView on that controller, I would expect the added view(s) to be wired up for viewWillAppear events.
Does anyone have an example of a complex programmatic view hierarchy that successfully receives viewWillAppear events at every level?
Apple's Docs state:
Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.
The problem is that they don't describe how to do this. What does "directly" mean? How do you "indirectly" add a view?
I am fairly new to Cocoa and iPhone so it would be nice if there were useful examples from Apple besides the basic Hello World crap.
If you use a navigation controller and set its delegate, then the view{Will,Did}{Appear,Disappear} methods are not invoked.
You need to use the navigation controller delegate methods instead:
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
I've run into this same problem. Just send a viewWillAppear message to your view controller before you add it as a subview. (There is one BOOL parameter which tells the view controller if it's being animated to appear or not.)
[myViewController viewWillAppear:NO];
Look at RootViewController.m in the Metronome example.
(I actually found Apple's example projects great. There's a LOT more than HelloWorld ;)
I finally found a solution for this THAT WORKS!
UINavigationControllerDelegate
I think the gist of it is to set your nav control's delegate to the viewcontroller it is in, and implement UINavigationControllerDelegate and it's two methods. Brilliant! I'm so excited i finally found a solution!
Thanks iOS 13.
ViewWillDisappear, ViewDidDisappear, ViewWillAppear and
ViewDidAppear won't get called on a presenting view controller on
iOS 13 which uses a new modal presentation that doesn't cover the
whole screen.
Credits are going to Arek Holko. He really saved my day.
I just had the same issue. In my application I have 2 navigation controllers and pushing the same view controller in each of them worked in one case and not in the other. I mean that when pushing the exact same view controller in the first UINavigationController, viewWillAppear was called but not when pushed in the second navigation controller.
Then I came across this post UINavigationController should call viewWillAppear/viewWillDisappear methods
And realized that my second navigation controller did redefine viewWillAppear. Screening the code showed that I was not calling
[super viewWillAppear:animated];
I added it and it worked !
The documentation says:
If you override this method, you must call super at some point in your implementation.
I've been using a navigation controller. When I want to either descend to another level of data or show my custom view I use the following:
[self.navigationController pushViewController:<view> animated:<BOOL>];
When I do this, I do get the viewWillAppear function to fire. I suppose this qualifies as "indirect" because I'm not calling the actual addSubView method myself. I don't know if this is 100% applicable to your application since I can't tell if you're using a navigation controller, but maybe it will provide a clue.
Firstly, the tab bar should be at the root level, ie, added to the window, as stated in the Apple documentation. This is key for correct behavior.
Secondly, you can use UITabBarDelegate / UINavigationBarDelegate to forward the notifications on manually, but I found that to get the whole hierarchy of view calls to work correctly, all I had to do was manually call
[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];
and
[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];
.. just ONCE before setting up the view controllers on the respective controller (right after allocation). From then on, it correctly called these methods on its child view controllers.
My hierarchy is like this:
window
UITabBarController (subclass of)
UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
UINavigationController (subclass of)
UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods
Just calling the mentioned methods on the tab/nav controller the first time ensured that ALL the events were forwarded correctly. It stopped me needing to call them manually from the UINavigationBarDelegate / UITabBarControllerDelegate methods.
Sidenote:
Curiously, when it didn't work, the private method
- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController
.. which you can see from the callstack on a working implementation, usually calls the viewWill/Did.. methods but didn't until I performed the above (even though it was called).
I think it is VERY important that the UITabBarController is at window level though and the documents seem to back this up.
Hope that was clear(ish), happy to answer further questions.
As no answer is accepted and people (like I did) land here I give my variation. Though I am not sure that was the original problem. When the navigation controller is added as a subview to a another view you must call the viewWillAppear/Dissappear etc. methods yourself like this:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[subNavCntlr viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[subNavCntlr viewWillDisappear:animated];
}
Just to make the example complete. This code appears in my ViewController where I created and added the the navigation controller into a view that I placed on the view.
- (void)viewDidLoad {
// This is the root View Controller
rootTable *rootTableController = [[rootTable alloc]
initWithStyle:UITableViewStyleGrouped];
subNavCntlr = [[UINavigationController alloc]
initWithRootViewController:rootTableController];
[rootTableController release];
subNavCntlr.view.frame = subNavContainer.bounds;
[subNavContainer addSubview:subNavCntlr.view];
[super viewDidLoad];
}
the .h looks like this
#interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
IBOutlet UIView *subNavContainer;
UINavigationController *subNavCntlr;
}
#end
In the nib file I have the view and below this view I have a label a image and the container (another view) where i put the controller in. Here is how it looks. I had to scramble some things as this was work for a client.
Views are added "directly" by calling [view addSubview:subview].
Views are added "indirectly" by methods such as tab bars or nav bars that swap subviews.
Any time you call [view addSubview:subviewController.view], you should then call [subviewController viewWillAppear:NO] (or YES as your case may be).
I had this problem when I implemented my own custom root-view management system for a subscreen in a game. Manually adding the call to viewWillAppear cured my problem.
Correct way to do this is using UIViewController containment api.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIViewController *viewController = ...;
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
}
I use this code for push and pop view controllers:
push:
[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];
pop:
[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];
.. and it works fine for me.
A very common mistake is as follows.
You have one view, UIView* a, and another one, UIView* b.
You add b to a as a subview.
If you try to call viewWillAppear in b, it will never be fired, because it is a subview of a
iOS 13 bit my app in the butt here. If you've noticed behavior change as of iOS 13 just set the following before you push it:
yourVC.modalPresentationStyle = UIModalPresentationFullScreen;
You may also need to set it in your .storyboard in the Attributes inspector (set Presentation to Full Screen).
This will make your app behave as it did in prior versions of iOS.
I'm not 100% sure on this, but I think that adding a view to the view hierarchy directly means calling -addSubview: on the view controller's view (e.g., [viewController.view addSubview:anotherViewController.view]) instead of pushing a new view controller onto the navigation controller's stack.
I think that adding a subview doesn't necessarily mean that the view will appear, so there is not an automatic call to the class's method that it will
I think what they mean "directly" is by hooking things up just the same way as the xcode "Navigation Application" template does, which sets the UINavigationController as the sole subview of the application's UIWindow.
Using that template is the only way I've been able to get the Will/Did/Appear/Disappear methods called on the object ViewControllers upon push/pops of those controllers in the UINavigationController. None of the other solutions in the answers here worked for me, including implementing them in the RootController and passing them through to the (child) NavigationController. Those functions (will/did/appear/disappear) were only called in my RootController upon showing/hiding the top-level VCs, my "login" and navigationVCs, not the sub-VCs in the navigation controller, so I had no opportunity to "pass them through" to the Nav VC.
I ended up using the UINavigationController's delegate functionality to look for the particular transitions that required follow-up functionality in my app, and that works, but it requires a bit more work in order to get both the disappear and appear functionality "simulated".
Also it's a matter of principle to get it to work after banging my head against this problem for hours today. Any working code snippets using a custom RootController and a child navigation VC would be much appreciated.
In case this helps anyone. I had a similar problem where my ViewWillAppear is not firing on a UITableViewController. After a lot of playing around, I realized that the problem was that the UINavigationController that is controlling my UITableView is not on the root view. Once I fix that, it is now working like a champ.
I just had this problem myself and it took me 3 full hours (2 of which googling) to fix it.
What turned out to help was to simply delete the app from the device/simulator, clean and then run again.
Hope that helps
[self.navigationController setDelegate:self];
Set the delegate to the root view controller.
In my case problem was with custom transition animation.
When set modalPresentationStyle = .custom viewWillAppear not called
in custom transition animation class need call methods:
beginAppearanceTransition and endAppearanceTransition
For Swift. First create the protocol to call what you wanted to call in viewWillAppear
protocol MyViewWillAppearProtocol{func myViewWillAppear()}
Second, create the class
class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
updatedCntllr.myViewWillAppear()
}
}
}
Third, make the instance of ForceUpdateOnViewAppear to be the member of the appropriate class that have the access to the Navigation Controller and exists as long as Navigation controller exists. It may be for example the root view controller of the navigation controller or the class that creates or present it. Then assign the instance of ForceUpdateOnViewAppear to the Navigation Controller delegate property as early as possible.
In my case that was just a weird bug on the ios 12.1 emulator. Disappeared after launching on real device.
I have created a class that solves this problem.
Just set it as a delegate of your navigation controller, and implement simple one or two methods in your view controller - that will get called when the view is about to be shown or has been shown via NavigationController
Here's the GIST showing the code
ViewWillAppear is an override method of UIViewController class so adding a subView will not call viewWillAppear, but when you present, push , pop, show , setFront Or popToRootViewController from a viewController then viewWillAppear for presented viewController will get called.
My issue was that viewWillAppear was not called when unwinding from a segue. The answer was to put a call to viewWillAppear(true) in the unwind segue in the View Controller that you segueing back to
#IBAction func unwind(for unwindSegue: UIStoryboardSegue, ViewController subsequentVC: Any) {
viewWillAppear(true)
}
I'm not sure this is the same problem that I solved.
In some occasions, method doesn't executed with normal way such as "[self methodOne]".
Try
- (void)viewWillAppear:(BOOL)animated
{
[self performSelector:#selector(methodOne)
withObject:nil afterDelay:0];
}
You should only have 1 UIViewController active at any time. Any subviews you want to manipulate should be exactly that - subVIEWS - i.e. UIView.
I use a simlple technique for managing my view hierarchy and have yet to run into a problem since I started doing things this way. There are 2 key points:
a single UIViewController should be used to manage "a screen's worth"
of your app
use UINavigationController for changing views
What do I mean by "a screen's worth"? It's a bit vague on purpose, but generally it's a feature or section of your app. If you've got a few screens with the same background image but different overlays/popups etc., that should be 1 view controller and several child views. You should never find yourself working with 2 view controllers. Note you can still instantiate a UIView in one view controller and add it as a subview of another view controller if you want certain areas of the screen to be shown in multiple view controllers.
As for UINavigationController - this is your best friend! Turn off the navigation bar and specify NO for animated, and you have an excellent way of switching screens on demand. You can push and pop view controllers if they're in a hierarchy, or you can prepare an array of view controllers (including an array containing a single VC) and set it to be the view stack using setViewControllers. This gives you total freedom to change VC's, while gaining all the advantages of working within Apple's expected model and getting all events etc. fired properly.
Here's what I do every time when I start an app:
start from a window-based app
add a UINavigationController as the window's rootViewController
add whatever I want my first UIViewController to be as the rootViewController of the nav
controller
(note starting from window-based is just a personal preference - I like to construct things myself so I know exactly how they are built. It should work fine with view-based template)
All events fire correctly and basically life is good. You can then spend all your time writing the important bits of your app and not messing about trying to manually hack view hierarchies into shape.