iPhone - отклонить несколько ViewControllers - PullRequest
43 голосов
/ 31 мая 2010

У меня длинная иерархия View Controllers;

в первом контроллере вида я использую этот код:

SecondViewController *svc = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
[self presentModalViewController:svc animated:YES];    
[svc release];

Во втором контроллере вида я использую этот код:

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self presentModalViewController:tvc animated:YES];    
[tvc release];

и т. Д.

Так что есть момент, когда у меня много View Controller, и мне нужно вернуться к первому View Controller. Если я возвращаюсь на один шаг сразу, я использую в каждом View Controller этот код:

[self dismissModalViewControllerAnimated:YES];

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

Спасибо

Ответы [ 22 ]

1 голос
/ 17 июня 2014

Используйте это общее решение для решения этой проблемы:

- (UIViewController*)topViewController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }
    return topController;
}


- (void)dismissAllModalController{

    __block UIViewController *topController = [self topViewController];

    while (topController.presentingViewController) {
        [topController dismissViewControllerAnimated:NO completion:^{

        }];
        topController = [self topViewController];
    }
}
1 голос
/ 26 сентября 2013
  id vc = [self presentingViewController];
  id lastVC = self;
  while (vc != nil) {
    id tmp = vc;
    vc = [vc presentingViewController];
    lastVC = tmp;
  }
  [lastVC dismissViewControllerAnimated:YES completion:^{
}];
1 голос
/ 23 февраля 2011

Прежде всего, Оскар Пели, спасибо за ваш код.

Чтобы запустить навигационный контроллер с самого начала, вы можете сделать его немного более динамичным. (если вы не знаете количество ViewControllers в стеке)

NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
1 голос
/ 28 января 2016

Простой рекурсивный доводчик:

extension UIViewController {
    final public func dismissEntireStackAndSelf(animate: Bool = true) {
        // Always false on non-calling controller
        presentedViewController?.ip_dismissEntireStackAndSelf(false)
        self.dismissViewControllerAnimated(animate, completion: nil)
    }
}

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

Вызов

baseController.dismissEntireStackAndSelf()
1 голос
/ 26 июня 2017

Для Swift 3.0 +

self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)

Это приведет к отклонению всех представленных контроллеров представления на вашем RootViewController.

1 голос
/ 14 декабря 2014

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

+ (UIViewController*)topmostViewController
{
    UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
    while(vc.presentedViewController) {
        vc = vc.presentedViewController;
    }
    return vc;
}

+ (void)returnToRootViewController
{
    UIViewController* vc = [UIViewController topmostViewController];
    while (vc) {
        if([vc isKindOfClass:[UINavigationController class]]) {
            [(UINavigationController*)vc popToRootViewControllerAnimated:NO];
        }
        if(vc.presentingViewController) {
            [vc dismissViewControllerAnimated:NO completion:^{}];
        }
        vc = vc.presentingViewController;
    }
}

Тогда я просто звоню

[UIViewController returnToRootViewController];
1 голос
/ 02 марта 2017

Свифт 3 расширение на основе вышеуказанных ответов.

Принцип для такого стека: A -> B -> C -> D

  • Сделайте снимок D
  • Добавить этот снимок на B
  • Уволить из B без анимации
  • По завершении увольнение из А с анимацией

    extension UIViewController {
    
        func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
            let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false)
            if !isBeingDismissed {
                var rootVc = presentingViewController
                while rootVc?.presentingViewController != nil {
                    rootVc = rootVc?.presentingViewController
                }
                let secondToLastVc = rootVc?.presentedViewController
                if fullscreenSnapshot != nil {
                    secondToLastVc?.view.addSubview(fullscreenSnapshot!)
                }
                secondToLastVc?.dismiss(animated: false, completion: {
                    rootVc?.dismiss(animated: true, completion: completion)
                })
            }
        }
    }
    

Немного мерцает на симуляторе, но не на устройстве.

1 голос
/ 05 августа 2016

Быстрое продление на основе приведенных выше ответов:

extension UIViewController {

    func dismissUntilAnimated<T: UIViewController>(animated: Bool, viewController: T.Type, completion: ((viewController: T) -> Void)?) {
        var vc = presentingViewController!
        while let new = vc.presentingViewController where !(new is T) {
            vc = new
        }
        vc.dismissViewControllerAnimated(animated, completion: {
            completion?(viewController: vc as! T)
        })
    }
}

Swift 3.0 версия:

extension UIViewController {

    /// Dismiss all modally presented view controllers until a specified view controller is reached. If no view controller is found, this function will do nothing.

    /// - Parameter reached:      The type of the view controller to dismiss until.
    /// - Parameter flag:         Pass `true` to animate the transition.
    /// - Parameter completion:   The block to execute after the view controller is dismissed. This block contains the instance of the `presentingViewController`. You may specify `nil` for this parameter.
    func dismiss<T: UIViewController>(until reached: T.Type, animated flag: Bool, completion: ((T) -> Void)? = nil) {
        guard let presenting = presentingViewController as? T else {
            return presentingViewController?.dismiss(until: reached, animated: flag, completion: completion) ?? ()
        }

        presenting.dismiss(animated: flag) {
            completion?(presenting)
        }
    }
}

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

0 голосов
/ 17 февраля 2017

Apple, документ о отклонить (анимированный: завершение:) метод.

В разделе Discussion сказано:

any intermediate view controllers are simply removed from the stack.

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

Другими словами, если стек контроллера представления выглядит следующим образом

Root -> A -> B -> C -> D ... -> Z

D вызывает dismiss метод, все контроллеры представления, кроме D, например: (E ... Z), будут удалены из стека.

0 голосов
/ 07 октября 2011

Если вы собираетесь вернуться к началу, вы можете использовать код [self.navigationController popToRootViewControllerAnimated: YES];

...