viewDidAppear not getting called
Asked Answered
U

9

10

In my main UIViewController I am adding a homescreen view controller as subviews:

   UINavigationController *controller = [[UINavigationController alloc] initWithRootViewController:vc];
        controller.navigationBarHidden = YES;
        controller.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
        [self addChildViewController:controller];
        [self.view insertSubview:controller.view atIndex:0];
        [controller didMoveToParentViewController:self];    

The issue is that viewDidAppear and viewWillAppear is only called once, just like viewDidLoad. Why is this? How do I make this work?

Basically inside vc I am not getting viewDidAppear nor viewWillAppear.

I also just tried adding the UIViewController without the navigation controller and it still doesn't work:

vc.view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
        [self addChildViewController:vc];
        [self.view insertSubview:vc.view atIndex:0];
        [vc didMoveToParentViewController:self];    
Underglaze answered 18/7, 2012 at 0:42 Comment(11)
Why are you using insertSubview:atIndex instead of just addSubview?Bitterroot
it's essentialy the same thing right? I tried insertSubview and it didn't make any differenceUnderglaze
Yes, essentially the same thing except addSubview adds at the end, and your insertSubview adds to the start, but that would only make a difference in the UI if there were other views already on the parent view controller.Gaeta
I don't understand the comment "viewDidAppear and viewWillAppear is only called once, just like viewDidLoad". Are you saying that the parent view controller is definitely getting it but the child view controllers aren't? Or are you saying that the child only gets it once (in which case I don't understand the question at all)?Gaeta
@RobertRyan so the parents viewDidAppear and viewWillAppear is also called once. So say I have a VC called MainViewController, which is the initial view controller set in storyboard. in my MainViewController I added the code written above (adding the child VC). Then in this child view controller (lets call it B) I am adding another child view controller, C. Basically I am expecting B's viewDidAppear to be called, when I remove C from B. Correct me I am wrong, but this is my issueUnderglaze
@Underglaze Ok. My child view controllers are getting viewWillAppear, fine, (unless I use that silly method I described in my answer below), so I'm perplexed. I don't know how complicated your project is but maybe you can try creating a new blank project with just the one trivially simple child view controller, and see if you still see the problem (that should be a 5-10 minute exercise). Assuming you don't, then maybe you can slowly add complexity to this test project (so it slowly, step by step, until you see the problem manifest itself again).Gaeta
@RobertRyan so with my example above. If I add C to B as a child VC. And I remove C from B. The question is, should B's viewDidAppear be called? I guess the question boils down to thisUnderglaze
@Underglaze Assuming B is yet another container controller, no, when you remove its child, C, you should not get viewDidAppear in B again. Because B is a container, it's assumed that it was (and still is) visible. Generally when I do containment, the child views don't take up the entire screen (because I have some control on the parent to dictate which child will be active), so the notion of not getting another viewWillAppear or viewDidAppear is intuitive. When your child is taking up the whole screen, I can understand why you would have expected the appearance methods, but you won't.Gaeta
By the way, how is C dismissing itself? removeFromParentViewController? According to the docs, "these methods are not intended to be called by clients [the child view controllers] of your container class". Thus, if your parent view controller B is removing child C, then you probably don't need any notification in B that it has appeared (because it knows, it just did it itself).Gaeta
No.. C is delegating back to B and then from B I am calling removeFromParentViewControllerUnderglaze
Good. But, still, B will not get appearance methods on the dismissal of child controller C. Only C would get the appearance methods relevant to C. B will not get any. But at least because B is removing C, you know exactly where you can hook in your relevant logic you wanted to have in B's viewWillAppear.Gaeta
Y
-10

Presenting view controllers using presentModalViewController or segues or pushViewController should fix it.

Alternatively, if for some reason you want to present your views without the built-in methods, in your own code you should be calling these methods manually. Something like this:

[self addChildViewController:controller];
BOOL animated = NO;
[controller viewWillAppear:animated];
[self.view insertSubview:controller.view atIndex:0];
[controller viewDidAppear:animated];
[controller didMoveToParentViewController:self];   
Yerkovich answered 18/7, 2012 at 0:53 Comment(4)
the issue is that this is only called once.. so what I basically want to have is when another UIViewController is added to vc then it should call viewDidAppearUnderglaze
+1 - I agree that if xonegirlz is using full screen views that she should think carefully whether view controller containment is really needed or whether a simpler push or modal model might make more sense, but given that she's invested all of this time and energy into containment, I assume she's got good reason to do so; I'm not crazy about the suggestion of viewWillAppear/viewDidAppear manually, though, as sometimes the lack of particular callbacks is a symptom of a bigger problem, and one shouldn't quickly gravitate to manually calling methods, fixing symptom only. But, creative idea.Gaeta
I agree with @Gaeta that needing to call viewWillAppear: and viewDidAppear: manually is "a symptom of a bigger problem" which this hack doesn't address and should be avoided.Determinate
You should never call viewWillAppear: etc methods manually, consider using beginAppearanceTransition:animated:, endAppearanceTransition methods insteadPraline
H
35

In my case, viewDidAppear was not called, because i have made unwanted mistake in viewWillAppear method.

-(void)viewWillAppear:(BOOL)animated {
    [super viewdidAppear:animated]; // this prevented my viewDidAppear method to be called
}
Hinkel answered 8/9, 2016 at 14:14 Comment(4)
Can't believe I did this as well... no wonder it was so hard to find people experiencing a similar issue!Concertante
Thanks a lot! I even did this mistake in a custom subclass of UIViewController and was was experiencing the problems in the class I derived from that subclass.Jacklyn
THANK YOU SO MUCH! hahaKevel
Had the same issue and I would have missed it if I didn't see this! Have an upvote kind sir.Surah
G
14

The only way I can reproduce the problem of child controllers not receiving appearance methods is if the container view controller does the following (which I'm sure you're not doing):

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers
{
    return NO;
}

Perhaps you can try explicitly enabling this:

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers
{
    return YES;
}

But my child view controllers definitely are getting the viewWillAppear calls (either if I explicitly automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers or if I omit this altogether.

Update:

Ok, looking at the comments under your original question, it appears that the issue is that the child controller (B) in question is, itself, a container view controller (which is perfectly acceptable) presenting another child controller (C). And this controller B's own child controller C is being removed and you're wondering why you're not getting viewWillAppear or viewDidAppear for the container controller B. Container controllers do not get these appearance methods when their children are removed (or, more accurately, since containers should remove children, not children removing themselves, when the container removes a child, it does not receive the appearance methods).

If I've misunderstood the situation, let me know.

Gaeta answered 18/7, 2012 at 4:2 Comment(5)
I assume that adding this automaticallyForward ... method with a return value of "YES" did nothing? I'd be surprised if it did, but worth the 60 second test...Gaeta
It worked for me...just adding automaticallyForwardAppearanceAndRotationMethodsToChildViewControllersHighkey
It also worked for me. I'm building an iOS 6,7 app and I'm not using storyboards. Wonder if that makes a difference? Very strange it would default to NO...Radioelement
As of iOS 6 automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers is deprecated in favor of two separate methods, shouldAutomaticallyForwardAppearanceMethods and shouldAutomaticallyForwardRotationMethods.Callus
Yep, and shouldAutomaticallyForwardRotationMethods was deprecated in iOS 8.Gaeta
E
11

@Rob answer in Swift 4 (which helped me on my case which I was adding a childViewController to a UITabBarController)

override var shouldAutomaticallyForwardAppearanceMethods: Bool {
    return true
}
Elver answered 4/10, 2017 at 15:37 Comment(2)
adding this to the host UITabBarController descendant solved my problem...thanks!Sharkskin
Glad it helped :)Elver
S
3

Another case where this will not be called at launch time (yet may be called on when you return to the view) will be is if you have subclassed UINavigationController and your subclass overrides

-(void)viewDidAppear:(BOOL)animated

but fails to call [super viewDidAppear:animated];

Showker answered 7/4, 2020 at 22:14 Comment(0)
H
1

Had a same problem My container view controller did retain a child view controller via a property, but did not add a child view controller to its childViewControllers array.

My solution was to add this line of code in the container view controller

[self addChildViewController: childViewController];

After that UIKit started forwarding appearance methods to my child view controller just as expected

I also changed the property attribute from strong to weak just for beauty

Holocrine answered 16/2, 2016 at 16:38 Comment(0)
C
1

When updating my code to 13.0, I lost my viewDidAppear calls.

In Objective-c, my solution was to add the following override all to the parent master view controller.

This allowed the ViewDidAppear call to get called once again...as it did in previous IOS (12 and earlier) version.

@implementation MasterViewController

//....some methods

  • (BOOL) shouldAutomaticallyForwardAppearanceMethods { return YES; }

// ...some methods

@end

Collude answered 4/10, 2019 at 17:57 Comment(1)
I see the override shouldAutomaticallyForwardAppearanceMethods not working at least in the case of tvOS 13+. Did it resolve for you in any other manner?Theme
C
0

My problem was that I was changing the tab in UITabBarController (selectedIndex = x) and then messing with the child view controllers in that tab. The problem is that it needs to be done the other way round: first mess with the child view controllers in other tab and then change the tab by setting the selectedIndex. After this change methods viewWillAppear/viewDidAppear begun to be called correctly.

Chelonian answered 8/4, 2014 at 0:57 Comment(0)
B
0

Here is another case when presenting viewController programmatically;

viewWillAppear or viewDidAppear not being called when presenting viewController with modalPresentationStyle .overCurrentContext, use .currentContext both functions will be triggered

let destinationVC = TestAppearanceViewController()
destinationVC.modalTransitionStyle = .coverVertical
destinationVC.modalPresentationStyle = .currentContext // Remove behind view controller and force presenter view controller calls both functions
vc.present(destinationVC, animated: true)
Bareilly answered 18/3 at 11:33 Comment(0)
Y
-10

Presenting view controllers using presentModalViewController or segues or pushViewController should fix it.

Alternatively, if for some reason you want to present your views without the built-in methods, in your own code you should be calling these methods manually. Something like this:

[self addChildViewController:controller];
BOOL animated = NO;
[controller viewWillAppear:animated];
[self.view insertSubview:controller.view atIndex:0];
[controller viewDidAppear:animated];
[controller didMoveToParentViewController:self];   
Yerkovich answered 18/7, 2012 at 0:53 Comment(4)
the issue is that this is only called once.. so what I basically want to have is when another UIViewController is added to vc then it should call viewDidAppearUnderglaze
+1 - I agree that if xonegirlz is using full screen views that she should think carefully whether view controller containment is really needed or whether a simpler push or modal model might make more sense, but given that she's invested all of this time and energy into containment, I assume she's got good reason to do so; I'm not crazy about the suggestion of viewWillAppear/viewDidAppear manually, though, as sometimes the lack of particular callbacks is a symptom of a bigger problem, and one shouldn't quickly gravitate to manually calling methods, fixing symptom only. But, creative idea.Gaeta
I agree with @Gaeta that needing to call viewWillAppear: and viewDidAppear: manually is "a symptom of a bigger problem" which this hack doesn't address and should be avoided.Determinate
You should never call viewWillAppear: etc methods manually, consider using beginAppearanceTransition:animated:, endAppearanceTransition methods insteadPraline

© 2022 - 2024 — McMap. All rights reserved.