IPhone View будет появляться не срабатывает

Я читал множество сообщений о людях, у которых возникают проблемы с 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.

У меня была эта проблема, пока я не понял, что неправильно понимаю предполагаемое использование подклассов UIViewController в целом. Проверьте этот вопрос. stackoverflow.com/questions/5691226/…

averydev 18.04.2011 03:26

Пожалуйста, будьте осторожны !!! Больше не актуально на iOS 5 !!! Вызовы viewWillAppear и viewDidAppear автоматически

Vilém Kurz 10.11.2011 21:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
117
2
102 101
27

Ответы 27

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

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

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

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

[myViewController viewWillAppear:NO];

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

(На самом деле я нашел великолепные примеры проектов Apple. Их НАМНОГО больше, чем HelloWorld;)

Фактически, вы должны вызвать viewWillAppear после того, как добавите его в подпредставление. В противном случае IBOutlets / IBActions не будут подключены.

4thSpace 17.06.2009 19:37

Да, потом. Создано подвид из XIB, viewWillAppear не был вызван. Позвонил сам, и все работает нормально.

JOM 16.02.2010 10:03

Спасибо! Это было как раз для меня. Я вручную добавлял подпредставление через [scrollView addSubview:controller.view];. Потом я добавил строку [controller viewWillAppear:NO]; и вуаля! Работал как шарм.

Rob S. 09.05.2010 22:43

По всей вероятности, это связано с тем, что ваш UIViewController управляет представлением, которое является подвидом представления, контролируемого другим UIViewController. Это не предполагаемый шаблон проектирования. Для получения дополнительных объяснений ознакомьтесь с этим постом. stackoverflow.com/questions/5691226/…

averydev 18.04.2011 03:28

Пожалуйста, будьте осторожны !!! Больше не актуально на iOS 5 !!! Вызывает viewWillAppear и viewDidAppear автоматически. Если вы вызываете его вручную, он будет вызван дважды.

Vilém Kurz 10.11.2011 21:24

С вложенным представлением мне все равно приходится вызывать это вручную даже для iOS7. Тогда это прекрасно работает.

juminoz 29.08.2013 05:02

Как, если дочерний контроллер представления добавлен из раскадровки?

Mubin Mall 07.01.2016 15:48

это никогда не было правильным - иногда вы вызываете viewWillAppear:, а система вызывает один и тот же метод. viewWillAppear: в вашем собственном классе всегда вызывает [super viewWillAppear:animated];. Повторный вызов может иметь непредсказуемый результат. И, конечно, почему вы выбрали существующий метод делегирования вместо своего собственного?

Vyachaslav Gerchicov 04.04.2017 18:21

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

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

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

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

Я не установил делегата своего контроллера навигации, но метод не был вызван. Во всяком случае, я установил его, а затем использовал методы, которые вы упомянули выше. Спасибо.

Dimitris 29.03.2010 16:53

Я вижу то же, что и Димитрис

jkp 14.04.2011 12:15

Я только что протестировал это в iOS4 и iOS5: это НЕ правда: установка делегата navigationController и последующее нажатие на него представления БУДЕТ срабатывать viewWillAppear: и т. д.

DaGaMs 07.11.2011 17:44

Swift 3: func navigationController (_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {} И func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {}

Naloiko Eugene 09.02.2017 16:52

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

У меня была такая же проблема. В моем приложении у меня есть 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.

То же самое и здесь. Облажался, не назвав супер.

olivaresF 03.08.2012 23:52

Я думаю, что они имеют в виду «напрямую», соединяя вещи точно так же, как это делает шаблон 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, не находится в корневом представлении. Как только я исправлю это, теперь он работает как чемпион.

Не могли бы вы рассказать, "как" вы это сделали?

Brabbeldas 01.03.2017 23:45

Представления добавляются «напрямую» по телефону [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

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

alt text

Наконец-то я нашел решение, ЧТО РАБОТАЕТ!

UINavigationControllerDelegate

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

как назначить rootviewcontroller в качестве делегата для контроллера навигации?

Gargo 23.10.2012 10:44

НЕ РАБОТАЕТ! попробуйте свернуть приложение и развернуть его

Vyachaslav Gerchicov 04.04.2017 18:14

Во-первых, панель вкладок должна находиться на корневом уровне, то есть добавляться к окну, как указано в документации 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 ключевых момента:

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

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

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

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

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

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

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

Нет ничего плохого в том, чтобы иметь несколько активных контроллеров представления; UIViewController имеет методы, позволяющие создавать иерархию (например, [addChildViewController:]).

Richard 20.09.2013 12:34

Да, сейчас. В 2011 году этого не произошло. Ответ был точным в то время, правда, не сейчас.

Nigel Flack 22.09.2013 20:10

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

Пытаться

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

проблема в том, что viewWillAppear вообще не вызывается

Vyachaslav Gerchicov 04.04.2017 18:16

Я использую этот код для контроллеров 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];

Eli Burke 22.09.2015 20:42

В этом случае viewWillAppear: по-прежнему может не называться.

Vyachaslav Gerchicov 04.04.2017 18:29

В моем случае проблема заключалась в пользовательской анимации перехода. При установке 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.

Вот GIST, показывающий код

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

Спасибо, iOS 13.

ViewWillDisappear, ViewDidDisappear, ViewWillAppear and ViewDidAppear won'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)
}

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