Я читал множество сообщений о людях, у которых возникают проблемы с viewWillAppear, когда вы неправильно создаете свою иерархию представлений только. Моя проблема в том, что я не могу понять, что это значит.
Если я создам RootViewController и вызову addSubView на этом контроллере, я ожидаю, что добавленные представления будут подключены к событиям viewWillAppear.
Есть ли у кого-нибудь пример сложной программной иерархии представлений, которая успешно принимает события viewWillAppear на каждом уровне?
Документы Apple заявляют:
Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.
Проблема в том, что они не описывают, как это сделать. Что значит «прямо»? Как вы «косвенно» добавляете представление?
Я новичок в Cocoa и iPhone, поэтому было бы неплохо, если бы помимо основной хрени Hello World были полезные примеры от Apple.
Пожалуйста, будьте осторожны !!! Больше не актуально на iOS 5 !!! Вызовы viewWillAppear и viewDidAppear автоматически





Я пользуюсь контроллером навигации. Когда я хочу либо перейти на другой уровень данных, либо показать свое собственное представление, я использую следующее:
[self.navigationController pushViewController:<view> animated:<BOOL>];
Когда я это делаю, у меня срабатывает функция viewWillAppear. Я полагаю, это квалифицируется как «косвенный», потому что я сам не вызываю фактический метод addSubView. Я не знаю, применимо ли это к вашему приложению на 100%, поскольку я не могу сказать, используете ли вы контроллер навигации, но, возможно, он даст подсказку.
Я столкнулся с той же проблемой. Просто отправьте сообщение viewWillAppear своему контроллеру представления, прежде чем добавлять его в качестве подпредставления. (Есть один параметр BOOL, который сообщает контроллеру представления, появляется ли он в анимации или нет.)
[myViewController viewWillAppear:NO];
Посмотрите на RootViewController.m в примере с метрономом.
(На самом деле я нашел великолепные примеры проектов Apple. Их НАМНОГО больше, чем HelloWorld;)
Фактически, вы должны вызвать viewWillAppear после того, как добавите его в подпредставление. В противном случае IBOutlets / IBActions не будут подключены.
Да, потом. Создано подвид из XIB, viewWillAppear не был вызван. Позвонил сам, и все работает нормально.
Спасибо! Это было как раз для меня. Я вручную добавлял подпредставление через [scrollView addSubview:controller.view];. Потом я добавил строку [controller viewWillAppear:NO]; и вуаля! Работал как шарм.
По всей вероятности, это связано с тем, что ваш UIViewController управляет представлением, которое является подвидом представления, контролируемого другим UIViewController. Это не предполагаемый шаблон проектирования. Для получения дополнительных объяснений ознакомьтесь с этим постом. stackoverflow.com/questions/5691226/…
Пожалуйста, будьте осторожны !!! Больше не актуально на iOS 5 !!! Вызывает viewWillAppear и viewDidAppear автоматически. Если вы вызываете его вручную, он будет вызван дважды.
С вложенным представлением мне все равно приходится вызывать это вручную даже для iOS7. Тогда это прекрасно работает.
Как, если дочерний контроллер представления добавлен из раскадровки?
это никогда не было правильным - иногда вы вызываете viewWillAppear:, а система вызывает один и тот же метод. viewWillAppear: в вашем собственном классе всегда вызывает [super viewWillAppear:animated];. Повторный вызов может иметь непредсказуемый результат. И, конечно, почему вы выбрали существующий метод делегирования вместо своего собственного?
Я не уверен в этом на 100%, но я думаю, что добавление представления в иерархию представлений напрямую означает вызов -addSubview: в представлении контроллера представления (например, [viewController.view addSubview:anotherViewController.view]) вместо того, чтобы помещать новый контроллер представления в стек контроллера навигации.
Если вы используете контроллер навигации и устанавливаете его делегата, то методы view {Will, Did} {Appear, Disappear} не вызываются.
Вместо этого вам нужно использовать методы делегата контроллера навигации:
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
Я не установил делегата своего контроллера навигации, но метод не был вызван. Во всяком случае, я установил его, а затем использовал методы, которые вы упомянули выше. Спасибо.
Я вижу то же, что и Димитрис
Я только что протестировал это в iOS4 и iOS5: это НЕ правда: установка делегата navigationController и последующее нажатие на него представления БУДЕТ срабатывать viewWillAppear: и т. д.
Swift 3: func navigationController (_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {} И func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {}
Я думаю, что добавление подпредставления не обязательно означает, что представление появится, поэтому нет автоматического вызова метода класса, который бы он
У меня была такая же проблема. В моем приложении у меня есть 2 контроллера навигации, и нажатие одного и того же контроллера представления в каждом из них сработало в одном случае, а не в другом. Я имею в виду, что при нажатии того же контроллера представления в первом UINavigationController вызывается viewWillAppear, но не при нажатии во втором контроллере навигации.
Потом наткнулся на этот пост UINavigationController должен вызывать методы viewWillAppear / viewWillDisappear
И понял, что мой второй контроллер навигации действительно изменил определение viewWillAppear. Проверка кода показала, что я не звоню
[super viewWillAppear:animated];
Я добавил, и все заработало!
В документации говорится:
If you override this method, you must call super at some point in your implementation.
То же самое и здесь. Облажался, не назвав супер.
Я думаю, что они имеют в виду «напрямую», соединяя вещи точно так же, как это делает шаблон xcode «Navigation Application», который устанавливает UINavigationController в качестве единственного подпредставления UIWindow приложения.
Использование этого шаблона - единственный способ получить методы Will / Did / Appear / Disappear, вызываемые для объекта ViewControllers при push / pop этих контроллеров в UINavigationController. Ни одно из других решений в ответах здесь не помогло мне, включая их реализацию в RootController и передачу их (дочернему) NavigationController. Эти функции (будут / появятся / появятся / исчезнут) вызывались только в моем RootController при отображении / скрытии VC верхнего уровня, моего "входа" и навигационных VC, а не суб-VC в контроллере навигации, поэтому у меня не было возможности "передать их" в Nav VC.
Я закончил тем, что использовал функциональность делегата UINavigationController для поиска конкретных переходов, которые требовали последующей функциональности в моем приложении, и это работает, но требует немного больше работы, чтобы «смоделировать» функциональность как исчезновения, так и появления.
Также это вопрос принципа - заставить его работать после того, как я часами бился головой об эту проблему сегодня. Мы будем очень благодарны за любые рабочие фрагменты кода, использующие настраиваемый RootController и дочерний навигационный VC.
Если это кому-то поможет. У меня была аналогичная проблема, когда мой ViewWillAppear не запускался на UITableViewController. После долгих экспериментов я понял, что проблема в том, что UINavigationController, который управляет моим UITableView, не находится в корневом представлении. Как только я исправлю это, теперь он работает как чемпион.
Не могли бы вы рассказать, "как" вы это сделали?
Представления добавляются «напрямую» по телефону [view addSubview:subview].
Представления добавляются «косвенно» такими методами, как панели вкладок или панели навигации, которые меняют подвиды.
Каждый раз, когда вы звоните в [view addSubview:subviewController.view], вы должны звонить в [subviewController viewWillAppear:NO] (или ДА, в зависимости от вашего случая).
У меня возникла эта проблема, когда я реализовал свою собственную систему управления корневым представлением для подэкрана в игре. Добавление вызова к viewWillAppear вручную устранило мою проблему.
[self.navigationController setDelegate:self];
Установите делегата на контроллер корневого представления.
Поскольку ответ не принимается и люди (как и я) приземляются здесь, я даю свой вариант. Хотя я не уверен, что это была изначальная проблема. Когда контроллер навигации добавляется в качестве подпредставления к другому представлению, вы должны сами вызвать методы 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
В файле пера у меня есть представление, а под ним - метка с изображением и контейнером (другое представление), куда я вставляю контроллер. Вот как это выглядит. Мне пришлось кое-что перемешать, так как это была работа для клиента.

Наконец-то я нашел решение, ЧТО РАБОТАЕТ!
UINavigationControllerDelegate
Я думаю, что суть этого заключается в том, чтобы установить делегата вашего навигационного элемента управления на контроллер представления, в котором он находится, и реализовать UINavigationControllerDelegate и его два метода. Блестяще! Я так взволнован, что наконец нашел решение!
как назначить rootviewcontroller в качестве делегата для контроллера навигации?
НЕ РАБОТАЕТ! попробуйте свернуть приложение и развернуть его
Во-первых, панель вкладок должна находиться на корневом уровне, то есть добавляться к окну, как указано в документации Apple. Это ключ к правильному поведению.
Во-вторых, вы может используете UITabBarDelegate / UINavigationBarDelegate для пересылки уведомлений вручную, но я обнаружил, что для правильной работы всей иерархии вызовов представления все, что мне нужно было сделать, это вызвать вручную
[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.
Примечание: Любопытно, что когда это не сработало, частный метод
- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController
.. который вы можете видеть из стека вызовов на работающей реализации, обычно вызывает методы viewWill/Did.., но не делал этого до тех пор, пока я не выполнил вышеупомянутое (даже если он был вызван).
Я думаю, что ОЧЕНЬ важно, чтобы UITabBarController находился на уровне окна, и документы, похоже, подтверждают это.
Надеюсь, что это было ясно (иш), рад ответить на дальнейшие вопросы.
У вас должен быть активен только 1 UIViewController в любое время. Любые подпредставления, которыми вы хотите манипулировать, должны быть именно такими - subVIEWS - то есть UIView.
Я использую простую технику для управления своей иерархией представлений, и с тех пор, как я начал делать это, я еще не столкнулся с проблемой. Есть 2 ключевых момента:
Что я имею в виду под "стоимостью экрана"? Это намеренно немного расплывчато, но, как правило, это функция или раздел вашего приложения. Если у вас есть несколько экранов с одинаковым фоновым изображением, но с разными наложениями / всплывающими окнами и т. д., Это должен быть 1 контроллер представления и несколько дочерних представлений. Вы никогда не должны работать с двумя контроллерами представления. Обратите внимание, что вы все равно можете создать экземпляр UIView в одном контроллере представления и добавить его в качестве подпредставления другого контроллера представления, если вы хотите, чтобы определенные области экрана отображались в нескольких контроллерах представления.
Что касается UINavigationController - это ваш лучший друг! Отключите панель навигации и укажите НЕТ для анимации, и у вас есть отличный способ переключения экранов по запросу. Вы можете нажимать и открывать контроллеры представления, если они находятся в иерархии, или вы можете подготовить массив контроллеров представления (включая массив, содержащий один VC) и установить его в качестве стека представления с помощью setViewControllers. Это дает вам полную свободу изменять VC, получая при этом все преимущества работы в рамках ожидаемой модели Apple и правильного запуска всех событий и т. д.
Вот что я делаю каждый раз, когда запускаю приложение:
(примечание, что начинать с оконного типа - это просто личное предпочтение - мне нравится конструировать вещи сам, поэтому я точно знаю, как они построены. Это должно нормально работать с шаблоном на основе представления)
Все события запускаются правильно, и в основном жизнь хороша. Затем вы можете потратить все свое время на написание важных частей своего приложения и не возиться с попытками вручную преобразовать иерархии представлений в форму.
Нет ничего плохого в том, чтобы иметь несколько активных контроллеров представления; UIViewController имеет методы, позволяющие создавать иерархию (например, [addChildViewController:]).
Да, сейчас. В 2011 году этого не произошло. Ответ был точным в то время, правда, не сейчас.
Не уверен, что это та же проблема, которую я решил. В некоторых случаях метод не выполняется обычным способом, например "[self methodOne]".
Пытаться
- (void)viewWillAppear:(BOOL)animated
{
[self performSelector:@selector(methodOne)
withObject:nil afterDelay:0];
}
проблема в том, что viewWillAppear вообще не вызывается
Я использую этот код для контроллеров push и pop view:
толкать:
[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];
поп:
[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];
.. и у меня он отлично работает.
У меня сама была эта проблема, и мне потребовалось 3 полных часа (2 из которых - поиск в Google), чтобы исправить ее.
Оказалось, что помогло просто удалите приложение с устройства / симулятора, очистите и снова запустите.
надеюсь, это поможет
Очень распространенная ошибка заключается в следующем.
У вас есть одно представление UIView* a и другое UIView* b.
Вы добавляете b в a как подпредставление.
Если вы попытаетесь вызвать viewWillAppear в b, он никогда не будет запущен, потому что это подпредставление
Правильный способ сделать это - использовать 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];
}
Это абсолютно правильное современное решение (iOS 9,8,7). Кроме того, если вы меняете встроенный контроллер представления на лету, вам нужно будет вызвать [viewController willMoveToParentViewController: nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController];
В этом случае viewWillAppear: по-прежнему может не называться.
В моем случае проблема заключалась в пользовательской анимации перехода.
При установке modalPresentationStyle = .customviewWillAppear не вызывается
в настраиваемом классе анимации перехода нужны методы вызова:
beginAppearanceTransition и endAppearanceTransition
Для Свифта. Сначала создайте протокол для вызова того, что вы хотите вызвать, в 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 свойству делегата контроллера навигации.
В моем случае это была просто странная ошибка в эмуляторе ios 12.1. Пропал после запуска на реальном устройстве.
Я создал класс, решающий эту проблему. Просто установите его в качестве делегата вашего контроллера навигации и реализуйте один или два простых метода в вашем контроллере представления, которые будут вызываться, когда представление будет показано или было показано через NavigationController.
ViewWillAppear - это метод переопределения класса UIViewController, поэтому добавление subView не будет вызывать viewWillAppear, но когда вы представляете, push, pop, show, setFront или popToRootViewController из viewController, тогда будет вызываться viewWillAppear для представленного viewController.
Спасибо, iOS 13.
ViewWillDisappear,ViewDidDisappear,ViewWillAppearandViewDidAppearwon't get called on a presenting view controller on iOS 13 which uses a new modal presentation that doesn't cover the whole screen.
Кредиты собираются в Арек Холко. Он действительно спас мне день.
iOS 13 воткнула мое приложение в зад. Если вы заметили изменение поведения в iOS 13, просто установите следующее, прежде чем нажимать его:
yourVC.modalPresentationStyle = UIModalPresentationFullScreen;
Вам также может потребоваться установить его в вашем .storyboard в Инспекторе атрибутов (установите для параметра «Презентация» значение «Полный экран»).
Это заставит ваше приложение вести себя так же, как в предыдущих версиях iOS.
Моя проблема заключалась в том, что viewWillAppear не вызывался при раскручивании из перехода. Ответ заключался в том, чтобы вызвать viewWillAppear (true) в сегменте раскрутки в контроллере представления, к которому вы возвращаетесь.
@IBAction func unwind (для unwindSegue: UIStoryboardSegue, ViewController followingVC: Any) {
viewWillAppear(true)
}
У меня была эта проблема, пока я не понял, что неправильно понимаю предполагаемое использование подклассов UIViewController в целом. Проверьте этот вопрос. stackoverflow.com/questions/5691226/…