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

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

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

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

Состояние Apple Docs:

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

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

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

Ответ 1

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

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

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

Ответ 2

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

[myViewController viewWillAppear:NO];

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

(Я действительно нашел примеры проектов Apple великолепно. Там LOT больше, чем HelloWorld;)

Ответ 3

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

UINavigationControllerDelegate

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

Ответ 4

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

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

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

[super viewWillAppear:animated];

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

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

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

Ответ 7

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

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

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

Ответ 8

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

alt text

Ответ 9

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

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

Ответ 10

Спасибо iOS 13.

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

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

enter image description here

Ответ 11

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

нажмите:

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

поп:

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

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

Ответ 12

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

Ответ 13

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

Ответ 14

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

Ответ 15

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

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

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

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

Ответ 16

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

Ответ 17

[self.navigationController setDelegate:self];

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

Ответ 18

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

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

Надеюсь, что поможет

Ответ 19

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

Ответ 20

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

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

Ответ 21

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

Ответ 22

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

Здесь GIST показывает код

Ответ 23

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

Ответ 24

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

yourVC.modalPresentationStyle = UIModalPresentationFullScreen;

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

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

Ответ 25

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

Try

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

Ответ 26

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

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

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

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

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

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

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

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

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