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

116

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

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

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

В Документах Apple говорится:

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

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

Я новичок в Cocoa и iPhone, поэтому было бы неплохо, если бы помимо основной хрени Hello World были полезные примеры от Apple.

chzk
источник
У меня была эта проблема, пока я не понял, что неправильно понимаю предполагаемое использование подклассов UIViewController в целом. Проверьте этот вопрос. stackoverflow.com/questions/5691226/…
averydev
7
Пожалуйста, будьте осторожны !!! Больше не актуально на iOS 5 !!! Звонки viewWillAppear и viewDidAppear автоматически
Вилем Курц

Ответы:

55

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

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

navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
mmalc
источник
2
Я не установил делегата своего контроллера навигации, но метод все равно не вызывается. В любом случае, я установил его, а затем использовал методы, которые вы упомянули выше. Спасибо.
Димитрис
Я вижу то же, что и Димитрис
jkp
7
Я только что протестировал это на iOS4 и iOS5: Это НЕ правда: установка делегата для navigationController и последующее нажатие на него представления БУДЕТ запускать viewWillAppear: и т. Д.
DaGaMs
Swift 3: func navigationController (_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {} И func navigationController (_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {}
Eugaloik,
28

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

[myViewController viewWillAppear:NO];

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

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

Лайош
источник
3
Собственно, вы должны вызвать viewWillAppear после того, как добавите его в подпредставление. В противном случае IBOutlets / IBActions не будут подключены.
4thSpace
2
Да, потом. Создано подвид из XIB, viewWillAppear не был вызван. Позвонил сам, и все работает нормально.
JOM
Спасибо! Это было как раз для меня. Я вручную добавлял подвид через [scrollView addSubview:controller.view];. [controller viewWillAppear:NO];Потом я добавил строчку и вуаля! Работал как шарм.
Роб С.
По всей вероятности, это связано с тем, что ваш UIViewController управляет представлением, которое является подвидом представления, контролируемого другим UIViewController. Это не предполагаемый шаблон проектирования. Для получения дополнительных объяснений ознакомьтесь с этим сообщением. stackoverflow.com/questions/5691226/…
averydev
6
Пожалуйста, будьте осторожны !!! Больше не актуально на iOS 5 !!! Вызывает viewWillAppear и viewDidAppear автоматически. Если вы вызываете его вручную, он будет вызван дважды.
Вилем Курц
18

Я наконец нашел решение, ЧТО РАБОТАЕТ!

UINavigationControllerDelegate

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

Крис
источник
как назначить rootviewcontroller в качестве делегата для контроллера навигации?
Gargo
1
НЕ РАБОТАЕТ! попробуйте свернуть приложение и развернуть его
Вячеслав Герчиков
8

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

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

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

[super viewWillAppear:animated];

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

В документации говорится:

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

Antoine
источник
То же самое и здесь. Облажался, не назвав супер.
olivaresF 03
5

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

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

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

Джош Ганьон
источник
5

Спасибо, iOS 13.

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

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

введите описание изображения здесь

BilalReffas
источник
4

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

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

Сэм
источник
3

Поскольку ответ не принимается и люди (как и я) приземляются здесь, я даю свой вариант. Хотя я не уверен, что это была изначальная проблема. Когда контроллер навигации добавляется в качестве подпредставления к другому представлению, вы должны сами вызвать методы 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

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

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

хол
источник
3

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

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

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

Andrews
источник
3

Правильный способ сделать это - использовать 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];
Эли Берк,
1
При этом дело viewWillAppear:еще может не называться
Вячеслав Герчиков
2

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

От себя:

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

поп:

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

.. и он отлично работает у меня.

Араш Зейноддини
источник
2

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

Giuseppe
источник
2

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

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

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

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

dollardime
источник
1

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

Мартин Гордон
источник
1

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


источник
1

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

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

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

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

Богатырь
источник
1

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

Андрей
источник
Не могли бы вы рассказать, "как" вы это сделали?
Brabbeldas 01
1

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

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

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

Финн Гайда
источник
1
[self.navigationController setDelegate:self];

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

Gaurav
источник
1

Для Свифта. Сначала создайте протокол для вызова того, что вы хотите вызвать, в 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 свойству делегата контроллера навигации.

Вадим Моторин
источник
0

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

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

обер
источник
0

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

coldembrace
источник
0

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

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

GregJaskiewicz
источник
0

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

Абу Уль Хасан
источник
0

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

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

   viewWillAppear(true)
}
user2385491
источник
-2

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

Пытаться

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}
Шон
источник
проблема viewWillAppearне называется вообще
Вячеслав Герчиков
-3

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

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

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

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

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

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

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

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

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

Найджел Флэк
источник
Нет ничего плохого в том, чтобы иметь несколько активных контроллеров представления; UIViewController имеет методы, позволяющие создавать иерархию (например, [addChildViewController:]).
Ричард
1
Да, сейчас. В 2011 году этого не произошло. Ответ был точным в то время, правда, сейчас нет.
Найджел Флэк