Benjohn
Benjohn

Reputation: 13887

viewDidAppear: called before view appears with nested UIViewController

The context

I have a view controller hierarchy:

  1. A container UIViewController: A with an embedded child UIPageViewController. A overlays various stuff on top of B and tinkers with the navigation, and other stuff.
  2. The embedded UIPageViewController: B.
  3. The pages: C that are displayed by B.

When A is instantiated, B is embedded in it. My override of prepareForSegue:sender in A causes C to be instantiated and set as B's current page:

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
  if([[segue identifier] isEqualToString: @"EmbedPagesSegue"])
  {
    UIPageViewController *const pagesViewController = [segue destinationViewController];
    [pagesViewController setDataSource: self];
    [pagesViewController setDelegate: self];
    [self setPagesViewController: pagesViewController];

    UIViewController *const initialPage = [self initialDisplayedPage];
    [pagesViewController 
      setViewControllers: @[initialPage] 
      direction: UIPageViewControllerNavigationDirectionForward 
      animated: NO 
      completion:nil];

    // Point X.
    // Segue not yet started.
    // A is not on screen yet!
    // C has already been added to B
  }
}

The Problem

C overrides viewDidAppearAnimated:.

When the initial page is presented, C's viewDidAppearAnimated: is called at Point X, before C's view can be on screen because B has yet to be added to A (the segue is just setting up).

This is a problem because C's implementations of viewDidAppearAnimated: needs to be able to reference the A that it has appeared in.

For subsequent pages, the embedding segue has completed, so B is added to A, and is on screen. In these cases the viewDidAppearAnimated: call to C is appropriate.

Or, to put it another way.

viewDidAppearAnimated: should actually be called viewDidAppearAnimatedInParentButTheParentMightNotHaveAppearedYet:

The question(s)

  1. Is there a clean way for C to access A during this initial set up process? I could do some kind of hack like defer the setup with performSelectorOnMainThread:withObject:waitUntilDone:, but I'd rather not.

  2. Am I implementing the embedding segue in the wrong way?

  3. Is this just a bad design – in which case, what instead?

Any other suggestions?

Upvotes: 0

Views: 379

Answers (1)

Léo Natan
Léo Natan

Reputation: 57060

Usually, it is a bad design to make assumptions about the view or controller hierarchy above the point you are at. So making assumptions about parent view controllers or superviews is normally not recommended. In your case, if you need to access A from C, create public API in C where you can pass A in some form (most likely, weakly), so it can configure itself.

Upvotes: 1

Related Questions