Развернуть переход к дочерним контроллерам представления, которые ведут себя не так, как ожидалось

Кажется, что переходы раскрутки ведут себя не так, как ожидалось, в iOS 8.1 в сочетании с модальным представлением и представлением контейнера. Вот иерархия контроллера представления для тестового проекта, которую можно найти на github:

Отключить изображение иерархии ошибок перехода

Нажатие на кнопку «Tap Me» вызывает модальное представление, которое встроено в контроллер навигации и имеет tableView в качестве дочернего контроллера представления. Нажатие на строку в tableView отправляет другой tableView. Наконец, нажатие на строку в этом последнем tableView должно вызвать переход с именем bUnwindSegue, найденный на предыдущем контроллере представления.

Проблемы:

  1. bUnwindSegue никогда не вызывается.
  2. Согласно техническому примечанию TN2298 контроллер представления контейнера отвечает за выбор дочернего контроллера представления для обработки перехода. В этом случае viewControllerForUnwindSegueAction:fromViewController:withSender: следует вызывать в контроллере представления контейнера. Это не так.

В примере проекта вы можете видеть, что BTableViewController содержит этап раскрутки:

- (IBAction)bUnwindSegue:(UIStoryboardSegue *)segue;
{
    NSLog(@"Unwinding...this unwind segue will never get called.");
}

В раскадровке действие выбора ячейки для CTableViewController действительно является bUnwindSegue. Также обратите внимание, что если вы измените действие выбора ячейки CTableViewController на раскрутку перехода в контроллере представления контейнера — containerVCUnwindSegue — этот переход будет вызван правильно.

Сегменты раскрутки ведут себя не так, как ожидалось?


person memmons    schedule 12.01.2015    source источник
comment
Ваш метод перехода к раскручиванию должен находиться в контроллере представления, на который вы раскручиваете to.   -  person Lyndsey Scott    schedule 12.01.2015
comment
@LyndseyScott BTableViewController — это VC, на который выполняется раскрутка.   -  person memmons    schedule 12.01.2015
comment
@LyndseyScott Переход к раскрутке в настоящее время задается с помощью раскадровки. Там тоже должны были произойти изменения. В качестве альтернативы вы можете удалить раскрутку раскадровки и установить ее программно. Я ожидаю, что оба случая будут вести себя одинаково.   -  person memmons    schedule 12.01.2015
comment
Только что понял, что неправильно прочитал второе предложение, на которое я ссылался... И я обычно путал B C и TableViewControllers... Сейчас я догнал и обдумал это..   -  person Lyndsey Scott    schedule 12.01.2015
comment
Причина, по которой он не переходит обратно, если вы не поместите переход в контроллер представления контейнера, заключается в том, что, в отличие от CTableViewController, BTableViewController не был добавлен в стек как автономный контроллер представления, а как контейнер вашего контроллера представления контейнера.   -  person Lyndsey Scott    schedule 12.01.2015
comment
@LyndseyScott Этот сценарий рассматривается в соответствии с TN2298.   -  person memmons    schedule 12.01.2015
comment
Проверил ваш код и вижу несколько проблем... Чуть позже напишу о них...   -  person Lyndsey Scott    schedule 13.01.2015


Ответы (1)


(1) Вы неправильно понимаете техническое примечание TN2298, которое вы процитировали, и (2) вы не отменяете viewControllerForUnwindSegueAction: должным образом.

Как указано в разделе документа TN2298, на который вы ссылались, о контроллере представления контейнера под его подзаголовком «Выбор контроллера дочернего представления для обработки действия раскрутки»:

Контроллер представления вашего контейнера должен переопределить метод, показанный в [viewControllerForUnwindSegueAction:], чтобы найти в своих дочерних контроллерах представления контроллер представления, который хочет обрабатывать действие раскрутки. Если ни один из дочерних контроллеров представления контейнера не хочет обрабатывать действие раскрутки, он должен вызвать реализацию super и вернуть результат.

Во-первых, чтобы переопределить метод, вы должны создать подкласс UINavigationController в своей раскадровке и добавить туда метод viewControllerForUnwindSegueAction:. После этого вы увидите, что метод теперь вызывается, как и ожидалось.

Но ваша вторая ошибка заключается в том, что ваша текущая попытка переопределить метод viewControllerForUnwindSegueAction: просто содержит return self;. Вместо этого вы должны вернуть контроллер представления, который вы хотели бы обработать действием раскрутки.

Например, у вас есть общедоступная переменная в VCWithContainedVCsViewController для хранения текущего экземпляра BTableViewController, и вы можете получить доступ к этому текущему контроллеру представления контейнера, например:

- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
{
    NSLog(@"Technical note TN2298 indicates child VCs defer to their parent to determine where an unwind segue will be handled.");

    if ([NSStringFromSelector(action) isEqualToString:@"bUnwindSegue:"]) {

        NSLog(@"%@", self.viewControllers);
        VCWithContainedVCsViewController *containerVC = (VCWithContainedVCsViewController*)self.viewControllers[0];
        return containerVC.container;
    }

    return [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
}

В этом случае вы увидите, что bUnwindSegue: на самом деле вызывается (ваше сообщение должно быть напечатано), но перехода все равно не произойдет.

Почему это?

Потому что, как я упоминал в комментариях, BTableViewController нет в текущем стеке навигации. Некоторые контроллеры дочерних представлений BTableViewController, такие как CTableViewController, будут находиться в стеке навигации, поскольку, например, CTableViewController не является контейнерным представлением. Но BTableViewController сам по себе не способен выполнить переход, потому что его нет в текущем стеке навигации. Таким образом, хотя вы можете фактически выбрать некоторые дочерние контроллеры представления для обработки действий раскрутки, как указано в документации, BTableViewController не будет одним из них.

person Lyndsey Scott    schedule 12.01.2015
comment
Вы неправильно понимаете, что происходит - viewControllerForUnwindSegueAction на VCWithContainedVCsViewController никогда не вызывается. Я возвращаю себя как заглушку и ставлю там точку останова для целей тестирования. - person memmons; 13.01.2015
comment
@ MichaelG.Emmons Я не ошибаюсь. Как я уже сказал, он вызывается не потому, что он не должен быть в классе вашего контроллера представления, а в подклассе вашего контроллера представления навигации: во-первых, чтобы переопределить метод, вы должны создать подкласс UINavigationController в своей раскадровке и добавить viewControllerForUnwindSegueAction: метод там. После этого вы увидите, что метод теперь вызывается, как и ожидалось. - person Lyndsey Scott; 13.01.2015
comment
Ах. Я вижу, что в этом ответе SO также рекомендуется создать подкласс навигационного контроллера (stackoverflow.com /вопросы/16092189/). Достаточно справедливо, но это прямо противоречит тому, что указано в техническом примечании, If you are using the container view controllers provided by the SDK, such as UINavigationController, these are handled automatically. - person memmons; 13.01.2015
comment
@ MichaelG.Emmons Нет, техническое примечание верно. Ваш навигационный контроллер пытается автоматически раскрутиться, как и предполагалось - просто (как я сказал в своем комментарии и в конце своего ответа) BTableViewController на самом деле не находится в стеке навигации, поэтому он не может выполнить раскрутку. Переопределяя автоматические действия контроллера UINavigation с помощью viewControllerForUnwindSegueAction: и заставляя выполнять переход в BTableViewController, даже если сообщение печатается, вы все равно увидите, что ничего не происходит. - person Lyndsey Scott; 13.01.2015
comment
@MichaelG.Emmons Также обратите внимание, что, поскольку BTableViewController не может выполнить раскрутку, UINavigationController (если viewControllerForUnwindSegueAction: не переопределен) будет продолжать просматривать стек навигации, поэтому, если вы поместите bUnwindSegue: в контейнер VC, он должен быть выполнен. (Нет необходимости создавать другой переход, как вы сделали для тестирования в текущем коде.) - person Lyndsey Scott; 13.01.2015