UIViewController viewWillAppear не вызывается при добавлении как subView

У меня есть UIViewController, который я загружаю из другого контроллера представления, а затем добавляю его представление к UIScrollView.

self.statisticsController = [self.storyboard instantiateViewControllerWithIdentifier:@"StatisticsViewController"];
self.statisticsController.match = self.match;

[self.scrollView addSubview:self.statisticsController.view];

Я поставил точки останова в контроллере просмотра статистики и viewDidLoad вызывается, но viewWillAppear не работает.

Это потому, что я не нажимаю его на иерархию или что-то еще?

Ответы

Ответ 1

Вы должны добавить statisticsController в качестве контроллера дочернего представления контроллера, представление которого вы добавляете.

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];

Я не уверен, что это вызовет вызов viewDidAppear, но вы можете переопределить didMoveToParentViewController: в дочернем контроллере, и это будет вызвано, поэтому вы можете поместить любой код, который вы положили бы в viewDidAppear там.

Ответ 2

Я столкнулся с -viewWillAppear: снова не вызванной проблемой. После гуглинга я приехал сюда. Я проверил несколько тестов и выяснил, что порядок вызова -addSubview и -addChildViewController: важен.

Случай 1. вызывает -viewWillAppear: контроллера, но Случай 2, он не будет вызывать -viewWillAppear:.

Случай 1:

  controller?.willMoveToParentViewController(self)

  // Call addSubview first
  self.scrollView.addSubview(controller!.view)
  self.addChildViewController(controller!)

  controller!.didMoveToParentViewController(self)

Случай 2:

  controller?.willMoveToParentViewController(self)

  // Call adChildViewController first      
  self.addChildViewController(controller!)      
  self.scrollView.addSubview(controller!.view)

  controller!.didMoveToParentViewController(self)

Ответ 3

По умолчанию обратные вызовы внешнего вида автоматически пересылаются детям. Он определяется с помощью свойства shouldAutomaticsForwardAppearanceMethods. Проверьте значение этого свойства, если оно НЕТ, и если ваш дочерний viewController должен отображаться прямо при появлении контейнера, вы должны уведомить дочерний процесс следующими способами в реализации жизненного цикла контроллера контейнера:

- (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];
}

Настройка внешнего вида и поведения обратного вызова

Исправлена моя проблема! Надеюсь, это будет полезно.

Ответ 4

Как уже упоминалось в другом ответе, родительский контроллер представления может не вызывать viewWillAppear и т.д., Если для параметра shouldAutomaticallyForwardAppearanceMethods задано значение false. UINavigationController и UITabBarController как известно, делают это. В этом случае вам нужно вызвать beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) на дочернем контроллере представления с isAppearing установленным в true когда представление появляется, и наоборот.

Вы должны размещать эти вызовы в соответствующих местах вашего кода, обычно, когда вы добавляете и удаляете дочерний контроллер представления.

Не забудьте вызвать endAppearanceTransition на вашем дочернем контроллере представления, когда ваш пользовательский переход завершится, иначе viewDidAppear и viewDidDisappear не будут вызваны.

Ответ 5

В Apple (https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html) правильный порядок вызовов API для добавления контроллера детского представления:

[self addChildViewController:childVC];
[self.view addSubview:childVC.view];
[childVC didMoveToParentViewController:self];

Но у меня все еще была проблема, когда viewWillAppear в дочернем VC не вызывался спорадически. Моя проблема заключалась в том, что было состояние гонки, которое могло привести к тому, что код выше был выполнен, прежде чем viewDidAppear в контроллере представления контейнера был вызван. Обеспечение того, чтобы viewDidAppear уже был вызван (или отложил добавление дочернего VC до его появления), решил для меня.

Ответ 6

Предыдущие ответы верны, но в случае, если это кому-то поможет - если вы переопределите loadView в дочернем контроллере представления, то не будет вызван ни один из других методов UIViewController.

Мне потребовалось некоторое время, чтобы понять, почему мой код не работает должным образом, пока я не понял, что случайно переопределил loadView вместо viewDidLoad.

Ответ 7

В моем случае я обнаружил, что неправильно переписал viewWillAppear: метод в моем базовом классе UINavigationController:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // other stuff
}

Обратите внимание, что вместо вызова super.viewWillAppear(animated) я написал super.viewWillDisappear(animated).

Это привело к UIViewController что все UIViewController которые были внутри этого UINavigationController не viewWillAppear: метод.

Надеюсь, что это поможет кому-то по аналогичной проблеме.