Отказаться от нескольких контроллеров модального вида одновременно? - PullRequest
21 голосов
/ 12 июля 2010

Таким образом, есть стек с тремя контроллерами представления, где A является корневым, B является первым контроллером модального представления, а C является третьим модальным vc. Я хотел бы перейти от C до A сразу. Я попытался это решение отклонить . Это работает работает , но не в правильном пути. То есть, когда последний контроллер вида отклоняется, он кратко покажет второй контроллер вида, прежде чем будет показан первый. То, что я ищу, - это способ перейти от третьего vc к первому в одной приятной анимации, не обращая внимания на второй вид. Любая помощь в этом очень ценится.

Ответы [ 5 ]

22 голосов
/ 12 июля 2010

Убедитесь, что вы звоните dismissModalViewControllerAnimated: только один раз.

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

У вас есть: A =modal> B =modal> C

Вам следует только позвонить [myViewControllerA dismissModalViewControllerAnimated:YES]

. Если вы используете [myViewControllerB dismissModalViewControllerAnimated:YES], он отклонит C, а не B. При обычном (unstacked) использовании он отклонит B(из-за цепочки респондента, пузырящейся сообщение до A).В описываемом вами стэкированном сценарии B является родительским контроллером представления, и он имеет преимущество перед модальным контроллером представления.

4 голосов
/ 27 мая 2016

Хотя принятый ответ сработал для меня, он может устареть и оставить странную анимацию, где самый верхний модал немедленно исчезнет, ​​и анимация будет на заднем модальном виде.Я перепробовал много вещей, чтобы избежать этого, и в итоге мне пришлось использовать немного хака, чтобы он выглядел красиво.Примечание: (протестировано только в iOS8 +, но должно работать в iOS7 +)

По сути, viewControllerA создает UINavigationController с viewControllerB в качестве корневого представления и представляет его модально.

// ViewControllerA.m
- (void)presentViewB {
    ViewControllerB *viewControllerB = [[ViewControllerB alloc] init];
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewControllerB];

    navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
    [self presentViewController:navigationController animated:YES completion:nil];
}

Теперь вviewControllerB мы собираемся представить viewControllerC таким же образом, но после того, как мы представим его, мы собираемся поместить снимок viewControllerC поверх слоя представления на контроллере навигации viewControllerB.Затем, когда viewControllerC исчезнет во время увольнения, мы не увидим изменения и анимация будет выглядеть красиво.

//ViewControllerB.m
- (void)presentViewC {
    ViewControllerC *viewControllerC = [[ViewControllerC alloc] init];

    // Custom presenter method to handle setting up dismiss and snapshotting 
    // I use this in a menu that can present many VC's so I centralized this part.
    [self presentViewControllerForModalDismissal:viewControllerC];
}

Ниже приведены мои вспомогательные функции, которые используются для представления вида и обработки увольнения.Стоит отметить, что я использую Purelayout для добавления ограничений автоматического макета.Вы можете изменить это, чтобы добавить их вручную или получить Purelayout в https://github.com/PureLayout/PureLayout

#pragma mark - Modal Presentation Helper functions
- (void)presentViewControllerForModalDismissal:(UIViewController*)viewControllerToPresent {
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewControllerToPresent];
    navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

    // Ensure that anything we are trying to present with this method has a dismissBlock since I don't want to force everything to inherit from some base class. 
    NSAssert([viewControllerToPresent respondsToSelector:NSSelectorFromString(@"dismissBlock")], @"ViewControllers presented through this function must have a dismissBlock property of type (void)(^)()");
    [viewControllerToPresent setValue:[self getDismissalBlock] forKey:@"dismissBlock"];

    [self presentViewController:navigationController animated:YES completion:^{
        // We want the presented view and this modal menu to dismiss simultaneous. The animation looks weird and immediately becomes the menu again when dismissing.
        // So we are snapshotting the presented view and adding it as a subview so you won't see the menu again when dismissing.
        UIView *snapshot = [navigationController.view snapshotViewAfterScreenUpdates:NO];
        [self.navigationController.view addSubview:snapshot];
        [snapshot autoPinEdgesToSuperviewEdges];
    }];
}

- (void(^)()) getDismissalBlock {
    __weak __typeof(self) weakSelf = self;
    void(^dismissBlock)() = ^{
        __typeof(self) blockSafeSelf = weakSelf;
        [blockSafeSelf.navigationController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    };

    return dismissBlock;
}

Теперь нам просто нужно убедиться, что dismissBlock определен как свойство в ViewControllerC.h (очевидно, вы можете заменить всю эту частьпри использовании методов делегата или других столь же интересных шаблонов проектирования важной частью является обработка увольнения на уровне viewControllerB)

// ViewControllerC.h
@interface ViewControllerC : UIViewController
@property (nonatomic, copy) void (^dismissBlock)(void);
@end

//ViewControllerC.m
// Make an method to handle dismissal that is called by button press or whatever logic makes sense.
- (void)closeButtonPressed {
    if (_dismissBlock)  {// If the dismissblock property was set, let the block handle dismissing
        _dismissBlock();
        return;
    }

    // Leaving this here simply allows the viewController to be presented modally as the base as well or allow the presenter to handle it with a block.
    [self dismissViewControllerAnimated:YES completion:nil];
}

Надеюсь, это поможет, счастливое программирование:)

2 голосов
/ 04 июня 2018

Для тех, кто ищет работу вокруг, вы можете сделать это:

  1. Накройте все снимком окна.
  2. Отключить оба контроллера представления без анимации.
  3. Представить копию снимка в другом контроллере представления без анимации.
  4. Удалить снимок, закрывающий окно.
  5. Отключить контроллер представления снимка с анимацией .

Вот код:

let window = UIApplication.shared.keyWindow!
let snapshot = window.snapshotView(afterScreenUpdates: false)!
window.addSubview(snapshot)

let baseViewController = self.presentingViewController!.presentingViewController!

baseViewController.dismiss(animated: false) {
    let snapshotCopy = snapshot.snapshotView(afterScreenUpdates: false)!
    let snapshotViewController = UIViewController()
    snapshotViewController.view.addSubview(snapshotCopy)

    baseViewController.present(snapshotViewController, animated: false) {
        snapshot.removeFromSuperview()
        baseViewController.dismiss(animated: true, completion: nil)
    }
}
0 голосов
/ 04 марта 2014

Вы можете отклонить эти modalViewControllers в вашем rootViewController.

    UIViewController *viewController = yourRootViewController;

    NSMutableArray *array = [NSMutableArray array];
    while (viewController.modalViewController) {
        [array addObject:viewController];
        viewController = viewController.modalViewController;
    }

    for (int i = 0; i < array.count; i++) {
        UIViewController *viewController = array[array.count-1-i];
        [viewController dismissModalViewControllerAnimated:NO];
    }
0 голосов
/ 12 июля 2010

То, что вы хотите использовать, это popToRootViewControllerAnimated:. Он возвращает вас к корневому контроллеру, не показывая все промежуточные.

...