Подтвердить что ты не робот

Невозможно создать сеанс размотки при использовании пользовательской оболочки контроллера просмотра

Я пытаюсь преобразовать наше приложение в раскадровки и попал в то, что, по моему мнению, является ошибкой при обработке развязанных сегментов при работе с пользовательскими контроллерами контейнеров. У нас есть контроллер представлений, который отображает другой, и для этого используется оболочка управления view view, я подключаю segue в IB, затем выбираю пользовательский класс для реализации. Метод выполнения выглядит примерно так:

-(void) perform {
    UIViewController *container = [self sourceViewController];
    UIViewController *child = [self destinationViewController];
    [container addChildViewController:child];
    [container.view addSubview:child.view];
    child.view.center = container.view.center;
    [UIView transitionWithView:container.view
                      duration:0.35
                       options:UIViewAnimationOptionCurveEaseInOut
                    animations:^{
                        child.view.alpha = 1;
                    } completion:^(BOOL finished) {
                        [child didMoveToParentViewController:container];
                    }];
}

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

-(UIViewController *) viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender {
    id default = [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
    NSAssert1(default == self, @"Expected the default view controller to be self but was %@", default);
    return default;
}

Я также могу подтвердить, что canPerformUnwindSegueAction: fromViewController: withSender вызывается и делает правильную вещь, но, чтобы быть уверенным, что он переназначил его, чтобы вернуть YES

-(BOOL) canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender {
    return YES;
}

Следующим шагом, который я ожидал бы, будет для segueForUnwindingToViewController: fromViewController: identifier: для вызова, однако это никогда не будет. Вместо этого приложение выходит из строя с помощью исключения NSInternalInconsistencyException.

2012-10-01 10:56:33.627 UnwindSegues[12770:c07] *** Assertion failure in -[UIStoryboardUnwindSegueTemplate _perform:], /SourceCache/UIKit_Sim/UIKit-2372/UIStoryboardUnwindSegueTemplate.m:78
2012-10-01 10:56:33.628 UnwindSegues[12770:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not find a view controller to execute unwinding for <USCustomContainerViewController: 0x75949a0>'
*** First throw call stack:
(0x1c8e012 0x10cbe7e 0x1c8de78 0xb61f35 0x581711 0x45ab54 0x10df705 0x16920 0x168b8 0xd7671 0xd7bcf 0xd6d38 0x4633f 0x46552 0x243aa 0x15cf8 0x1be9df9 0x1be9ad0 0x1c03bf5 0x1c03962 0x1c34bb6 0x1c33f44 0x1c33e1b 0x1be87e3 0x1be8668 0x1365c 0x1e7d 0x1da5)
libc++abi.dylib: terminate called throwing an exception

Кто-нибудь успешно использовал unind segues в сочетании с API-интерфейсами API-интерфейса контроллера? Любая идея, какой шаг мне не хватает? Я загрузил демонстрационный проект в github, который показывает проблему в простейшем демонстрационном проекте, который я мог бы придумать.

4b9b3361

Ответ 1

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

У меня есть рабочий пример, показывающий, что canPerformUnwindSegueAction действительно обсуждается с родительской цепочкой и что viewControllerForUnwindSegueAction и segueForUnwindingToViewController вызываются и эффективны, если они присутствуют в нужном месте. См:

https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch19p640presentedViewControllerStoryboard2

Теперь я создал вилку вашего исходного примера на github, исправляя ее так, чтобы она иллюстрировала эти функции:

https://github.com/mattneub/UnwindSegues

На самом деле это не такая ситуация, когда требуется "расслабиться", но она показывает, как "разматывать" можно использовать, когда задействован пользовательский контроллер представления контейнера.

Ответ 2

Кажется, что это ошибка - я также ожидал бы, что вы сможете работать, как только вы это сделали.

Обходной путь, который я использовал, явно отклоняет представленный контроллер представления в методе IBAction:

- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
                                      fromViewController:(UIViewController *)fromViewController
                                              identifier:(NSString *)identifier
{
    return [[UIStoryboardSegue alloc] initWithIdentifier:identifier
                                                 source:fromViewController
                                             destination:toViewController];
}

- (IBAction)unwind:(UIStoryboardSegue*)segue
{
    UIViewController *vc = segue.sourceViewController;
    [vc willMoveToParentViewController:nil];
    if ([vc respondsToSelector:@selector(beginAppearanceTransition:animated:)]) {
        [vc beginAppearanceTransition:NO animated:YES]; // iOS 6
    }
    UIView *modal = vc.view;
    UIView *target = [[segue destinationViewController] view];
    [UIView animateWithDuration:duration animations:^{
        modal.frame = CGRectMake(0, target.bounds.size.height, modal.frame.size.width, modal.frame.size.height);
     } completion:^(BOOL finished) {
        [modal removeFromSuperview];
        [vc removeFromParentViewController];
        if ([vc respondsToSelector:@selector(endAppearanceTransition)]) {
            [vc endAppearanceTransition];
          }
    }];
}

Ответ 3

Краткая история перед ответом: я просто столкнулся с тем же самым точным сообщением об ошибке при попытке использовать несколько видов контейнеров на одном экране iPad в iOS 6 и вызывая разворот segues из кода. Сначала я подумал, что это может быть проблемой, потому что мой segue был создан с использованием Storiesboards путем CTRL-перетаскивания из File Owner в Exit, а не из некоторого элемента управления пользовательского интерфейса в Exit, но у меня были такие же результаты, когда я добавлял теги Close на каждом VC и они запускают разматывание. Я понял, что я пытаюсь отключить встроенный сегмент, а не модальный /push/popup segue, поэтому имеет смысл, что он этого не делает. В конце концов, если развязка segue завершается успешно, а контроллер представления выгружается из представления контейнера, iOS 6 считает, что на этом месте будет пустое место на экране. (В моем случае у меня есть другой вид контейнера, занимающий экранную недвижимость, которая отображается за контейнером, который я пытаюсь выгрузить, но iOS не знает, что, поскольку эти два не связаны каким-либо образом.)

Ответ: это привело меня к осознанию того, что вы можете только размотать модальные, push или popper segues, будь то в главном окне или как часть контроллера навигации/табуляции. Это b/c iOS, а затем знает, что был предыдущий VC, ответственный за весь экран, и безопасно вернуться к нему. Таким образом, в вашем случае я хотел бы узнать, как iOS показывает, что представление вашего дочернего контейнера связано с представлением вашего родительского контейнера таким образом, чтобы было безопасно отклонить представление дочернего контейнера. Например, выполните режим modal/push/popover segue при отображении представления дочернего контейнера или оберните оба в пользовательский класс UINavigationController (я предполагаю, что вам не нужна панель навигации, почему пользовательский класс).

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

Ответ 4

Похоже, эта ошибка исправлена ​​в iOS9.