2

I have a view controller which queries a web service as to whether an interstitial ad should be shown. If so, another view controller is instantiated and presented using presentViewController:animated:completion:. According to this answer and the docs, I would assume viewDidAppear: would not be called when dismissing the presented view controller (which it does itself). Conceptually, to me anyway, the presenting view controller's view is never removed from the view hierarchy and therefore never needs to "reappear". I'm obviously wrong. So what is going on? Why is what I'm seeing differing from what the docs say?

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [[AdService sharedAdService] adForSlotName:@"Main Interstitial" completionBlock:^(Ad *adForSlotName) {

        if(adForSlotName)
        {
            InterstitialAdViewController_iPhone *interstitialAdViewController = [[InterstitialAdViewController_iPhone alloc] init];
            interstitialAdViewController.ad = adForSlotName;

            dispatch_queue_t mainQueue = dispatch_get_main_queue();
            dispatch_async(mainQueue, ^{

                [self presentViewController:interstitialAdViewController animated:YES completion:^{}];

            });

            [interstitialAdViewController release];
        }
    }];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [[AdService sharedAdService] clearAdForSlotName:@"Main Interstitial"];

    [super viewWillDisappear:animated];
}
Community
  • 1
  • 1
rob5408
  • 2,972
  • 2
  • 40
  • 53
  • Which view is receiving the viewDidAppear at what point? To me it seems unclear in your question. I would assume that viewDidAppear is invoked whenever a view appears. Appearing could be something that happens once a view is presented but also would happen on a underlying view once the view formerly covering it is removed (dismissed). – Till May 26 '13 at 18:27
  • Apologies, the viewDidAppear: on the presenting view controller is called whenever presented view controller dismisses. – rob5408 May 26 '13 at 18:37
  • that would be matching my expectations as drafted in the second part of my first comment and is exactly as it should be. – Till May 26 '13 at 18:40
  • Thanks Till, I mention in a comment below that my problem started with viewWillDisappear: but I assumed I could frame my question in terms of viewDidAppear:. Maybe I make too big of a logical leap thinking that for viewDidAppear: to be called the view would had to have been "disappeared" or never appeared in the first place. – rob5408 May 26 '13 at 18:53

2 Answers2

5

The answer to which you linked is about the behavior of viewDidDisappear, not viewDidAppear. The note in the documentation you linked says that viewDidAppear will not be called on the presenting view controller when a child view controller presented with a popover is dismissed.

In your case you are presenting the child view controller without a popover, so I read the doc's note (in an "exception that proves the rule" way) as saying that viewDidAppear should be called on the presenting view controller when the child view controller is dismissed in your case.

I think you should be seeing viewDidDisappear called on your parent view controller when the child view controller is presented. I tried this out in a minimal test app, using this code to present a child view controller:

Parent *parent = [[Parent alloc] initWithNibName:nil bundle:nil];
[self.window setRootViewController:parent];
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    Child *child = [[Child alloc] initWithNibName:nil bundle:nil];
    [parent presentViewController:child animated:YES completion:^{
        NSLog(@"Child presented");
        double delayInSeconds = 2.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [parent dismissViewControllerAnimated:YES completion:^{
                NSLog(@"Child dismissed");
            }];
        });
    }];
});

and, logging the calls to the various appearance related callbacks in both the Parent and Child class. This is the sequence I see:

Making the parent view controller the root view controller:

Parent viewWillAppear:
Parent viewDidAppear:

Presenting the child view controller:

Parent viewWillDisappear:
Child viewWillAppear:
Child viewDidAppear:
Parent viewDidDisappear:
Child presented

Dismissing the child view controller:

Child viewWillDisappear:
Parent viewWillAppear:
Parent viewDidAppear:
Child viewDidDisappear:
Child dismissed

So this seems internally consistent to me---the parent gets the disappearance calls when the child view controller is presented and the appearance calls when the child view controller is dismissed. And this is still consistent with the documented behavior of viewWillAppear because I'm not presenting the child view controller with a popover.

I'm afraid I don't know why you're not getting the viewWill/DidDisappear calls when your child view controller is presented.

Aaron Golden
  • 7,092
  • 1
  • 25
  • 31
  • In truth, my problem started with viewWillDisappear: being called when presenting a view controller, but thought maybe it was more of a symptom of something else going on. So I framed the question from the perspective of my viewWillAppear. Maybe I should rewrite the entire question. Thanks. – rob5408 May 26 '13 at 18:48
  • Hi Aaron, one more comment. To me it seems strange that if the expected behavior is to not call viewDidDisappear when presenting a view controller then the original view controller should also NOT call viewWillAppear upon dismissing of the presented view controller. Do you agree? – rob5408 May 26 '13 at 18:56
  • I agree that seems strange, and in fact when I experimented with this I got the viewDidDisappear calls on presenting the child view controller (see my edit). – Aaron Golden May 26 '13 at 19:11
  • This is fantastic, thanks. The problem had to do with my interpretation of the docs and that other question. – rob5408 May 26 '13 at 19:26
  • @rob5408 you may have lost me here but I felt that it might be worth noting that appearance callbacks being called inconsistently often in fact relates to a defective view controller hierachy. I have seen such things happening when people try to reinvent view controller containment on older iOS versions, a lot. Please also note that the order of appearance callbacks is iOS version specific and not exactly documented. – Till May 26 '13 at 20:57
  • @Till, I was actually seeing consistent behavior but was apparently reading the docs and that other answer wrong. Or maybe reading the docs right, but that other answer might just be wrong. Anyway, Aaron confirmed I wasn't completely crazy with what I was seeing. Thanks for the help! – rob5408 May 27 '13 at 02:57
3

I have experienced the same behaviour. The viewDidAppear: method is called when dismissing presented view controller. However, there is a way out. Create a boolean variable inside your controller, set it to NO in viewDidLoad and check it in viewDidAppear:. Then set it to YES. Like so:

#import "MyViewController.h"

@implementation MyViewController {
    BOOL alreadyShown;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    alreadyShown = NO;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    if (!alreadyShown) {
        alreadyShown = YES;
        //do your stuff
    }
}

@end
Adam
  • 26,549
  • 8
  • 62
  • 79