UIViewController viewWillAppear not called when adding as subView
Asked Answered
M

9

35

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?

Miscount answered 14/8, 2013 at 15:2 Comment(3)
Yip, if a view is added as a subview, viewDidAppear will not be called. You could get around it by manually calling it from the parent views viewDidAppear methodArsenical
https://mcmap.net/q/428447/-viewdidappear-for-subviewsArsenical
Ah, I thought that might be the case. Thanks.Miscount
H
54

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.

Hyunhz answered 14/8, 2013 at 15:8 Comment(8)
Would this trigger viewDidAppear methods?Arsenical
Hey! This works :D Thanks very much! @Arsenical yes it triggers the viewWillAppear method.Miscount
This will only work on iOS 6, on iOS 5 you will have to call viewWillAppear: manually.Brnaba
There should be [self.statisticsController willMoveToParentViewController:self] call before [self addChildViewController:self.statisticsController].Ressieressler
@RafałAugustyniak, That's not necessary, it's called automatically by the system. From the docs: "When your custom container calls the addChildViewController: method, it automatically calls the willMoveToParentViewController: method of the view controller to be added as a child before adding it."Hyunhz
trouble is ... it triggers viewWillAppear twice!! which sucksJoust
Actually you must first addChildViewController, then addSubview.Schargel
you must first addChildViewController... @AlexanderVolkov is right.Prestidigitation
F
50

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)
Fabrikoid answered 26/8, 2016 at 7:46 Comment(8)
Interesting. This contradicts Apple's documentation in the view controller programming guide. You should file a bug.Xenogamy
This helped me.Hemicellulose
Any update on this? Seems like a bug to me, but definitely ran into the same thing. Apple's documentation states case 2 is the correct way, and it makes more sense (developer.apple.com/library/content/featuredarticles/…). Maybe I'll file a radar...Annoy
@SeeMeCode, please file a radar. :) I has not touched those codes about 1 year.Fabrikoid
Case 2 seems to be working fine. (Xcode 9.3, Swift 4.1)Popery
You have saved my day!Thermosetting
it doesn't seem like a bug. UIKit checks shouldAutomaticallyForwardAppearanceMethods flag and call willAppear inside viewWillMoveToWindow methods. If childController hasn't been added yet UIKit 'can't understand" added view is view of childController so there is not enough information to start "appearence forwarding".Hartzke
I can add that this bug happened to us on iOS 10. Worked fine on iOS 11 later.Lorri
G
22

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.

Guadalajara answered 26/6, 2015 at 9:44 Comment(0)
B
6

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.

Balaam answered 23/4, 2018 at 15:15 Comment(0)
L
3

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.

Lauter answered 24/11, 2016 at 7:46 Comment(0)
E
1

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.

Equuleus answered 22/3, 2018 at 5:51 Comment(0)
P
1

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.

Perceive answered 26/11, 2019 at 8:50 Comment(0)
S
0

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.

Short answered 3/12, 2019 at 5:35 Comment(0)
N
0

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)
Negrillo answered 20/12, 2020 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.