Strange behaviour in object-c IOS 8 on method calling - ios

I have a view controller where
- (BOOL)prefersStatusBarHidden {
return YES;
}
is getting called before
- (void)viewDidLoad
{
[super viewDidLoad];
}
I am another view controller where the viewDidLoad is getting called first and then the prefersStatusBarHidden.
I want prefersStatusBarHidden to be called before viewDidLoad.
Please help!

for a simple view controller in a tab bar controller I got this order for iOS 8.3
13:57:46.610 loadView
13:57:46.612 viewDidLoad
13:57:46.612 updateViewConstraints
13:57:46.613 viewWillLayoutSubviews
13:57:46.613 viewDidLayoutSubviews
13:57:46.614 viewWillAppear:
13:57:46.616 prefersStatusBarHidden
13:57:46.616 viewWillLayoutSubviews
13:57:46.616 viewDidLayoutSubviews
13:57:46.712 viewDidAppear:
The reason you see the viewWillLayoutSubviews/viewDidLayoutSubviews calls twice, that any transitioning animation is happening in between.
I would expect that if there is a animation, also the disappearing of a statusbar would be animated and that might be the reason the call for prefersStatusBarHidden happens there.
So whatever you want to do in viewDidLoad is better suited in viewWillLayoutSubviews or viewDidLayoutSubviews
I used this code: https://github.com/vikingosegundo/ofaexample/blob/298c56346f49c467c9f54e9cf18cd5ec604c1fdc/OFAExample/SecondViewController.m

This seems to be the order the methods are called:
initWithCoder:
awakeFromNib
willMoveToParentViewController:
prefersStatusBarHidden
preferredStatusBarUpdateAnimation
loadView
prepareForSegue:sender:
viewDidLoad
extendedLayoutIncludesOpaqueBars
edgesForExtendedLayout
viewWillAppear:
...
Source: https://bradbambara.wordpress.com/2014/07/31/object-life-cycle-uiviewcontroller/
So you need to rethink your architecture so you're not assuming viewDidLoad is called before prefersStatusBarHidden

Related

viewWillAppear called but viewDidAppear not called after pushViewController

In my app, sometimes pushViewController fails for no reason and what happens is very weird. The navigationBar and navigationItem change but the ViewController is not pushed. Then I can tap nothing on the screen. I find that viewWillAppear is called but viewDidAppear isn't called. I push the home button of iPhone to enter background. After entering foreground again, the ViewController is pushed and viewDidAppear is called. I don't know why and when it happens.
normal viewDidAppear callstack
viewDidAppear after enterBackground callstack
If you can repro by:
Try using the left edge pop gesture when there are no view
controllers below it (i.e on root view controllers, your VC-Home
controller)
Try clicking on any UI elements after this.
Then Disable interactivePopGestureRecognizer when current viewController is the firstVC in navigation controller.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
reference:
iOS App Freezes on PushViewController
Is the viewcontroller which you pushed into the view hierarchy overwrite the viewWillAppear/viewDidAppear accidently without calling [super viewWillAppear/viewDidAppear:animated]?
You're probably accidentally calling [super viewDidLoad] inside your viewWillAppear method
For anyone having the same issue as me: Check all your custom views to see if you're not having an infinite loop of layoutSubviews. This is on of the things that happens in between a viewWillAppear and a viewDidAppear.
In my implementation I had a custom Tab bar controller and fore some reason viewDid Appear was empty, putting super calling solved it.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) //was missing this line
}
You know,if you overwrite navigationController.interactivePopGestureRecognizer.delegate and not restore it appropriately, this bug will appear,too

UIViewController: detecting when view appears again

I have a UIViewController class that contains a WKWebView and implements WKNavigationDelegate.
I would like to detect when a the view controller appears again. I understand the method loadView but, if I push a new view on the stack and then go back from that view to the previous view (my view controller) which method is called on the view controller?
The method that will be called is viewWillAppear:.
If you push to next view then viewDidLoad will be called first
Then viewWillAppear, viewDidAppear
If you pop to previous screen again (your UIViewController) then
viewWillAppear will be called first and after entire view appears
then viewDidAppear will be called..
viewDidAppear is useful in the cases where any method called at viewWillAppear after that you can Load the data at ViewDidAppear..
The ViewControllers viewDidLoad method is only called once when the view is created for the first time.
// viewDidLoad is called only once when the view is created for the first time
- (void) viewDidLoad
{
[super viewDidLoad];
// do your code here
}
You can also implement the below two methods in side your ViewController.m class
// viewWillAppear is called just before the view is about to be appeared
- (void) viewWillAppear
{
[super viewWillAppear];
// do your code here
}
// is called when the view has appeared
- (void) viewDidAppear
{
[super viewDidAppear];
// do your code here
}

viewWillAppear and viewDidAppear called - viewDidLoad not

I have a ViewController that calls viewWillAppear and viewDidAppear without calling viewDidLoad.
How can this be?

viewWillAppear, viewDidAppear and viewWillDisappear not being called on iOS 5 when using setViewControllers

I'm setting a ViewController using the following code: [detailNav setViewControllers:[NSArray arrayWithObject:vc] animated:NO];
When the ViewController is set in iOS 6 and 7, viewWillAppear, viewDidAppear and viewWillDisappear is called.
However, in iOS 5, these methods are not called.
Edit: pushViewController does not work either:
viewWillDisappear and viewDidDisappear never get called
Turns out somewhere in the ViewControllers hierarchy, automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers was returning NO.
Returning YES fixed the issue.
Shot in the dark but make sure each has the super call example in the viewdidload method it would have this first
[super viewDidLoad];

UIViewController viewWillAppear not called when adding as subView

I have a UIViewController that I am loading from inside another view controller and then adding its view to a UIScrollView.
self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:#"StatisticsViewController"];
self.statisticsController.match = self.match;
[self.scrollView addSubview:self.statisticsController.view];
I've put breakpoints in the statistics view controller and viewDidLoad is being called but viewWillAppear isn't.
Is it because I'm not pushing it onto the hierarchy or something?
You should add statisticsController as a child view controller of the controller whose view you're adding it to.
self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:#"StatisticsViewController"];
self.statisticsController.match = self.match;
[self.scrollView addSubview:self.statisticsController.view];
[self addChildViewController:self.statisticsController];
[self.statisticsController didMoveToParentViewController:self];
I'm not sure this will make viewDidAppear get called, but you can override didMoveToParentViewController: in the child controller, and that will be called, so you can put any code that you would have put in viewDidAppear in there.
I encounter -viewWillAppear: not called problem again. After googling, I came here. I did some tests, and find out that the calling order of -addSubview and -addChildViewController: is important.
Case 1. will trigger -viewWillAppear: of controller, but Case 2, it WON'T call -viewWillAppear:.
Case 1:
controller?.willMoveToParentViewController(self)
// Call addSubview first
self.scrollView.addSubview(controller!.view)
self.addChildViewController(controller!)
controller!.didMoveToParentViewController(self)
Case 2:
controller?.willMoveToParentViewController(self)
// Call adChildViewController first
self.addChildViewController(controller!)
self.scrollView.addSubview(controller!.view)
controller!.didMoveToParentViewController(self)
By default, appearance callbacks are automatically forwarded to children.
It's determined with shouldAutomaticallyForwardAppearanceMethods property. Check value of this propery, if it's NO and if your child viewController should appear right on container's appearance, you should notify child with following methods in container's controller life-cycle implementation:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
for (UIViewController *child in self.childViewControllers) {
[child beginAppearanceTransition:YES animated:animated];
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.child endAppearanceTransition];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
for (UIViewController *child in self.childViewControllers) {
[child beginAppearanceTransition:NO animated:animated];
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.child endAppearanceTransition];
}
Customizing Appearance and Rotation Callback Behavior
Fixed my problem! Hope it would be helpful.
As mentioned in another answer, the parent view controller might not call viewWillAppear etc. when shouldAutomaticallyForwardAppearanceMethods is set to false. UINavigationController and UITabBarController are known to do that. In this case, you need to call beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) on the child view controller with isAppearing set to true when the view appears and vice versa.
You have to place these calls at appropriate places in your code, normally when you add and remove your child view controller.
Don't forget to call endAppearanceTransition on your child view controller when your custom transition has ended, otherwise viewDidAppear and viewDidDisappear are not called.
Per Apple (https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html), the correct order of API calls to add a child view controller is:
[self addChildViewController:childVC];
[self.view addSubview:childVC.view];
[childVC didMoveToParentViewController:self];
But I still had the problem where viewWillAppear in the child VC was not sporadically getting called. My issue was that there was a race condition that could cause the code above to get executed before viewDidAppear in the container view controller was called. Ensuring that viewDidAppear had already been called (or deferring the addition of the child VC until it was) solved it for me.
The previous answers are correct, but in case it helps someone - if you override loadView in the child view controller, then none of the other UIViewController methods get called.
Took me some time to realize why my code wasn't running properly, until I realized that I had accidentally overridden loadView instead of viewDidLoad.
Check if your parent VC is a UINavigationViewController (or any other container). In this case the shouldAutomaticallyForwardAppearanceMethods is False and the appearance methods are not called.
I can't understand your questions and your description.
My problem was similar to this only.
CustomTabBarController -> CustomUINavigationController -> RootViewcontroller
viewWillAppear of CustomUINavigationController and RootViewController are not getting called unless you switched to another tab and come back.
The solution is call super.viewWillAppear(animated: true)
override func viewWillAppear(_ animated: Bool) {
**super.viewWillAppear(true)**
}
I struggled for more than a day for this small mistake.
View appearance methods also will not get forwarded if your view controller hasn't loaded its view. This could happen if you override loadView in your child view controller, and the view is already added to the view hierarchy.
In that case, you could do
addChild(childVC)
childVC.loadViewIfNeeded()
childVC.didMove(toParent: self)

Resources