iPhone viewWillAppear не стреляет

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

Если я создам RootViewController и позвонить addSubView на этом контроллере, я бы ожидал, что добавленные представления будут подключены для viewWillAppear События.

У кого-нибудь есть пример сложной программной иерархии представлений, которая успешно получает viewWillAppear события на каждом уровне?

Документы Apple заявляют:

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

Проблема в том, что они не описывают, как это сделать. Что значит "напрямую"? Как вы "косвенно" добавляете представление?

Я довольно новичок в Какао и iPhone, так что было бы неплохо, если бы были полезные примеры от Apple, помимо основной хрени Hello World.

24 ответа

Если вы используете контроллер навигации и задаете его делегат, то методы представления {Will,Did}{Appear,Disappear} не запускаются.

Вместо этого вам нужно использовать методы делегата контроллера навигации:

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:

Я столкнулся с этой же проблемой. Просто отправьте viewWillAppear сообщение на ваш контроллер представления, прежде чем добавить его в качестве подпредставления. (Существует один параметр BOOL, который сообщает контроллеру представления, должен ли он быть анимированным, чтобы появиться или нет.)

[myViewController viewWillAppear:NO];

Посмотрите на RootViewController.m в примере Metronome.

(Я на самом деле нашел примеры проектов Apple великолепными. Существует намного больше, чем HelloWorld;)

Я наконец нашел решение для этого, что работает!

UINavigationControllerDelegate

Я думаю, что суть этого в том, чтобы установить делегата вашего элемента управления навигацией на viewcontroller, в котором он находится, и реализовать UINavigationControllerDelegate и это два метода. Brilliant! Я так взволнован, я наконец нашел решение!

Спасибо, iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppear а также ViewDidAppear не будет вызываться в контроллере представления представления в iOS 13, который использует новое модальное представление, которое не покрывает весь экран.

Кредиты идут Ареку Холко. Он действительно спас мне день.

У меня просто была такая же проблема. В моем приложении у меня есть 2 контроллера навигации и нажатие одного и того же контроллера представления в каждом из них работал в одном случае, а не в другом. Я имею ввиду, что при нажатии на тот же контроллер вида в первом UINavigationController, viewWillAppear был вызван, но не при нажатии на второй контроллер навигации.

Тогда я наткнулся на этот пост UINavigationController должен вызывать методы viewWillAppear / viewWillDisappear

И понял, что мой второй контроллер навигации сделал переопределение viewWillAppear, Скрининг кода показал, что я не звонил

[super viewWillAppear:animated];

Я добавил это, и это сработало!

В документации сказано:

Если вы переопределите этот метод, вы должны вызвать super в какой-то момент вашей реализации.

Я использовал навигационный контроллер. Когда я хочу перейти на другой уровень данных или показать свой пользовательский вид, я использую следующее:

[self.navigationController pushViewController:<view> animated:<BOOL>];

Когда я делаю это, я получаю viewWillAppear функция стрелять Я предполагаю, что это квалифицируется как "косвенный", потому что я не называю фактическое addSubView сам метод. Я не знаю, применимо ли это на 100% к вашему приложению, так как не могу сказать, используете ли вы навигационный контроллер, но, возможно, это даст подсказку.

Во-первых, панель вкладок должна быть на корневом уровне, т.е. добавлена ​​в окно, как указано в документации Apple. Это ключ к правильному поведению.

Во-вторых, вы можете использовать UITabBarDelegate / UINavigationBarDelegate для пересылки уведомлений вручную, но я обнаружил, что для правильной работы всей иерархии вызовов view мне нужно было только вручную

[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];

а также

[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];

.. только ОДИН РАЗ перед настройкой контроллеров представления на соответствующем контроллере (сразу после выделения). С тех пор он корректно вызывал эти методы на своих дочерних контроллерах представления.

Моя иерархия такая:

window
    UITabBarController (subclass of)
        UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
            UINavigationController (subclass of)
                UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods

Простой вызов упомянутых методов на контроллере tab/nav в первый раз гарантировал, что ВСЕ события были перенаправлены правильно. Это остановило меня от необходимости вызывать их вручную из UINavigationBarDelegate / UITabBarControllerDelegate методы.

Sidenote: Любопытно, что когда это не сработало, частный метод

- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController 

.. который вы можете увидеть из callstack на работающей реализации, обычно вызывает viewWill/Did.. методы, но не, пока я не выполнил вышеупомянутое (даже при том, что это было вызвано).

Я думаю, что ОЧЕНЬ важно, чтобы UITabBarController находится на уровне окна, хотя и документы, кажется, подтверждают это.

Надеюсь, что было ясно (иш), с удовольствием отвечу на дальнейшие вопросы.

Поскольку никакого ответа не принято, и люди (как и я) приземляются здесь, я даю свой вариант. Хотя я не уверен, что это была первоначальная проблема. Когда контроллер навигации добавляется как подпредставление к другому представлению, вы должны сами вызывать методы viewWillAppear/Dissappear и т. Д., Например, так:

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

    [subNavCntlr viewWillAppear:animated];
}

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

    [subNavCntlr viewWillDisappear:animated];
}

Просто чтобы завершить пример. Этот код появляется в моем ViewController, где я создал и добавил навигационный контроллер в представление, которое я поместил в представление.

- (void)viewDidLoad {

    // This is the root View Controller
    rootTable *rootTableController = [[rootTable alloc]
                 initWithStyle:UITableViewStyleGrouped];

    subNavCntlr = [[UINavigationController alloc]   
                  initWithRootViewController:rootTableController];

    [rootTableController release];

    subNavCntlr.view.frame = subNavContainer.bounds;

    [subNavContainer addSubview:subNavCntlr.view];

    [super viewDidLoad];
}

.h выглядит так

@interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
    IBOutlet UIView *subNavContainer;
    UINavigationController *subNavCntlr;
}

@end

В nib-файле у меня есть вид, а под этим видом у меня есть метка изображения и контейнер (другой вид), куда я помещаю контроллер. Вот как это выглядит. Я должен был кое-что исправить, так как это было работой для клиента.

альтернативный текст

Представления добавляются "напрямую" по вызову [view addSubview:subview], Представления добавляются "косвенно" такими методами, как панели вкладок или панели навигации, которые меняют подвиды.

Каждый раз, когда вы звоните [view addSubview:subviewController.view]Вам следует позвонить [subviewController viewWillAppear:NO] (или ДА в зависимости от вашего случая).

У меня была эта проблема, когда я реализовал свою собственную систему управления корневыми представлениями для подэкрана в игре. Добавление вызова к viewWillAppear вручную вылечило мою проблему.

Правильный способ сделать это - использовать API-интерфейс UIViewController.

- (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
     UIViewController *viewController = ...;
     [self addChildViewController:viewController];
     [self.view addSubview:viewController.view];
     [viewController didMoveToParentViewController:self];
}

Очень распространенная ошибка заключается в следующем. У вас есть одно мнение, UIView* a, и еще один, UIView* b, Вы добавляете b в a как подпредставление. Если вы попытаетесь вызвать viewWillAppear в b, он никогда не будет запущен, потому что это подпредставление

I OS 13 воткнула мое приложение в зад. Если вы заметили изменение поведения в iOS 13, просто установите следующее, прежде чем нажимать:

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

Вам также может потребоваться установить его в своем.storyboard в инспекторе атрибутов (установите для параметра "Презентация" значение "Полный экран").

Это заставит ваше приложение вести себя так же, как в предыдущих версиях iOS.

Я использую этот код для контроллеров push и pop view:

От себя:

[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];

поп:

[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];

.. и у меня все работает нормально.

Для Свифта. Сначала создайте протокол для вызова того, что вы хотели вызвать в viewWillAppear

protocol MyViewWillAppearProtocol{func myViewWillAppear()}

Во-вторых, создать класс

class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
    if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
        updatedCntllr.myViewWillAppear()
    }
}

}

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

[self.navigationController setDelegate:self];

Установите делегата на корневой контроллер представления.

Я думаю, что добавление подпредставления не обязательно означает, что представление появится, поэтому нет автоматического вызова метода класса, который будет

У меня просто была эта проблема, и мне потребовалось 3 полных часа (2 из которых - поиск в Google), чтобы решить ее.

Оказалось, что помогло просто удалить приложение с устройства / симулятора, очистить, а затем снова запустить.

надеюсь, это поможет

Я не уверен на 100% в этом, но я думаю, что добавление представления в иерархию представлений напрямую означает вызов -addSubview: на представлении контроллера представления (например, [viewController.view addSubview:anotherViewController.view]) вместо помещения нового контроллера вида в стек контроллера навигации.

Я думаю, что они имеют в виду "напрямую", подключая вещи точно так же, как это делает шаблон xcode "Navigation Application", который устанавливает UINavigationController в качестве единственного подпредставления UIWindow приложения.

Использование этого шаблона - единственный способ, с помощью которого я смог получить методы Will/Did/Appear/Disappear, вызываемые для объекта ViewControllers, после нажатия / выталкивания этих контроллеров в UINavigationController. Ни одно из других решений в ответах здесь не работало для меня, включая реализацию их в RootController и передачу их через (дочерний) NavigationController. Эти функции (будут / делались / появляются / исчезают) вызывались в моем RootController только при отображении / скрытии ВК высшего уровня, моих "логинов" и навигационных ВК, а не суб-ВК в контроллере навигации, поэтому у меня не было возможности "передать их" в Nav VC.

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

Кроме того, принципиально важно заставить его работать после того, как я сегодня часами бьюсь об эту проблему. Будем весьма благодарны за любые фрагменты рабочего кода, использующие пользовательский RootController и дочерний VC для навигации.

В моем случае проблема была с пользовательской анимацией перехода. Когда установлено modalPresentationStyle = .customviewWillAppear не называется

В пользовательском классе анимации перехода нужны методы вызова:beginAppearanceTransition а также endAppearanceTransition

На случай, если это кому-нибудь поможет. У меня была похожая проблема, где мой ViewWillAppear не стреляет по UITableViewController, После долгой игры я понял, что проблема в том, что UINavigationController это контролирует мой UITableView не в корневом представлении. Как только я это исправлю, теперь он работает как чемпион.

Я создал класс, который решает эту проблему. Просто установите его в качестве делегата вашего контроллера навигации и внедрите простой один или два метода в вашем контроллере представления - которые будут вызываться, когда представление будет показано или было показано через NavigationController

Вот ГИСТ, показывающий код

В моем случае это была просто странная ошибка в эмуляторе ios 12.1. Исчез после запуска на реальном устройстве.

ViewWillAppear - это метод переопределения класса UIViewController, поэтому добавление subView не вызовет viewWillAppear, но когда вы представляете, push, pop, show, setFront или popToRootViewController из viewController, тогда вызывается viewWillAppear для представленного viewController.

Моя проблема заключалась в том, что viewWillAppear не вызывался при раскручивании из перехода. Ответ заключался в том, чтобы вызвать viewWillAppear(true) в сегменте раскрутки в контроллере представления, к которому вы возвращаетесь.

@IBAction func unwind(для unwindSegue: UIStoryboardSegue, ViewController followingVC: Any) {

   viewWillAppear(true)
}

Я не уверен, что это та же проблема, которую я решил.
В некоторых случаях метод не выполняется обычным способом, таким как "[self methodOne]".

Пытаться

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}

В любое время у вас должен быть активен только 1 UIViewController. Любые подпредставления, которыми вы хотите манипулировать, должны быть именно такими - subVIEWS - т.е. UIView.

Я использую простую технику для управления своей иерархией представлений, и мне еще не приходилось сталкиваться с проблемой, так как я начал все делать таким образом. Есть 2 ключевых момента:

  • один UIViewController должен использоваться для управления "стоимостью экрана" вашего приложения
  • используйте UINavigationController для смены представлений

Что я имею в виду под "стоимостью экрана"? Это немного расплывчато, но обычно это функция или раздел вашего приложения. Если у вас есть несколько экранов с одним и тем же фоновым изображением, но с разными наложениями / всплывающими окнами и т. Д., Это должен быть 1 контроллер представления и несколько дочерних представлений. Вы никогда не должны работать с двумя контроллерами представления. Обратите внимание, что вы все еще можете создать экземпляр UIView в одном контроллере представления и добавить его в качестве подпредставления другого контроллера представления, если вы хотите, чтобы определенные области экрана отображались в нескольких контроллерах представления.

Что касается UINavigationController - это ваш лучший друг! Отключите панель навигации и укажите "Нет" для анимации, и у вас есть отличный способ переключения экранов по требованию. Вы можете выдвигать и выдвигать контроллеры представления, если они находятся в иерархии, или вы можете подготовить массив контроллеров представления (включая массив, содержащий один VC) и установить его в качестве стека представления, используя setViewControllers. Это дает вам полную свободу менять ВК, одновременно используя все преимущества работы в рамках ожидаемой модели Apple и обеспечения надлежащего запуска всех событий и т. Д.

Вот что я делаю каждый раз, когда запускаю приложение:

  • начать с оконного приложения
  • добавить UINavigationController в качестве rootViewController окна
  • добавить все, что я хочу, чтобы мой первый UIViewController был rootViewController для контроллера nav

(примечание, начинающееся с окон, это просто личное предпочтение - мне нравится создавать вещи самостоятельно, поэтому я точно знаю, как они создаются. Это должно хорошо работать с шаблоном на основе представления)

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

Другие вопросы по тегам