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

IPhone - отклонение нескольких ViewControllers

У меня есть длинная иерархия 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, и мне нужно вернуться к первому контроллеру View. Если я вернусь на один шаг сразу, я использую в каждом View Controller этот код:

[self dismissModalViewControllerAnimated:YES];

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

Спасибо

4b9b3361

Ответ 1

Я нашел решение.

Конечно, вы можете найти решение в наиболее очевидном месте, поэтому читайте с помощью ссылки UIViewController для метода rejectModalViewControllerAnimated...

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

поэтому достаточно вызвать функцию rejectModalViewControllerAnimated на целевом представлении. Я использовал следующий код:

[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];

чтобы вернуться в мой дом.

Ответ 2

Да. уже есть куча ответов, но я все равно добавлю один из них в конец списка. Проблема в том, что нам нужно получить ссылку на контроллер вида в базе иерархии. Как и в ответе @Juan Munhoes Junior, вы можете ходить по иерархии, но могут быть разные маршруты, которые пользователь мог бы взять, чтобы получить довольно хрупкий ответ. Нетрудно расширить это простое решение, хотя просто пройти иерархию, ища дно стека. Призыв к увольнению на нижней части тоже получит все остальные.

-(void)dismissModalStack {
    UIViewController *vc = self.presentingViewController;
    while (vc.presentingViewController) {
        vc = vc.presentingViewController;
    }
    [vc dismissViewControllerAnimated:YES completion:NULL];
}

Это просто и гибко: если вы хотите найти определенный тип контроллера представления в стеке, вы можете добавить логику на основе [vc isKindOfClass:[DesiredViewControllerClass class]].

Ответ 3

Универсальный метод iOS 8+ для полноэкранного увольнения без неправильного контекста анимации. В Objective-C и Swift

Objective-C

- (void)dismissModalStackAnimated:(bool)animated completion:(void (^)(void))completion {
    UIView *fullscreenSnapshot = [[UIApplication sharedApplication].delegate.window snapshotViewAfterScreenUpdates:false];
    [self.presentedViewController.view insertSubview:fullscreenSnapshot atIndex:NSIntegerMax];
    [self dismissViewControllerAnimated:animated completion:completion];
}

Swift

func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
    if let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false) {
        presentedViewController?.view.addSubview(fullscreenSnapshot)
    }
    if !isBeingDismissed {
        dismiss(animated: animated, completion: completion)
    }
}

TL;DR

Что не так с другими решениями?

Существует много решений, но ни один из них не считается с неправильным контекстом отклонения, поэтому:

например. корень A → Представляет B → Представляет C, и вы хотите отклонить его до A с C, вы можете официально позвонить, нажав dismissViewControllerAnimated на rootViewController.

 [[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];

Однако отклонение вызова от этого корня с C приведет к правильному поведению с неправильным переходом (от B до A будет видно вместо C на A).


, поэтому

Я создал универсальный метод увольнения. Этот метод примет текущий полноэкранный снимок и поместит его поверх ресивера, представленного контроллером представления, и затем отбросит все его. (Пример: вызывается увольнение по умолчанию с C, но B действительно рассматривается как отклонение)

Ответ 4

Скажите, что ваш первый контроллер представлений также является контроллером Root/Initial View (тот, который вы назначили на своем раскадровке в качестве начального контроллера просмотра). Вы можете настроить его для прослушивания запросов, чтобы отклонить все представленные им контроллеры представлений:

в FirstViewController:

- (void)viewDidLoad {
    [super viewDidLoad];

    // listen to any requests to dismiss all stacked view controllers
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissAllViewControllers:) name:@"YourDismissAllViewControllersIdentifier" object:nil];

    // the remainder of viewDidLoad ...
}

// this method gets called whenever a notification is posted to dismiss all view controllers
- (void)dismissAllViewControllers:(NSNotification *)notification {
    // dismiss all view controllers in the navigation stack
    [self dismissViewControllerAnimated:YES completion:^{}];
}

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

[[NSNotificationCenter defaultCenter] postNotificationName:@"YourDismissAllViewControllersIdentifier" object:self];

Это должно отклонить все модально представленные контроллеры представлений с анимацией, оставив только контроллер корневого представления. Это также работает, если ваш начальный контроллер представления является UINavigationController, а первый контроллер представления установлен как его контроллер корневого представления.

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

Ответ 5

[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];

Вы также можете реализовать делегат во всех контроллерах, которые вы хотите отклонить.

Ответ 6

Если вы используете все контроллеры View Model, вы можете использовать уведомление для отклонения всех предустановленных контроллеров представления.

1. Уведомление о регистрации в RootViewController, как это

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(dismissModelViewController)
                                             name:dismissModelViewController
                                           object:nil];

2.Удалить функцию rejectModelViewController в rootviewController

- (void)dismissModelViewController
{
    While (![self.navigationController.visibleViewController isMemberOfClass:[RootviewController class]])
    {
        [self.navigationController.visibleViewController dismissViewControllerAnimated:NO completion:nil];
    }
}

3.Нотификация после каждого события закрытия или закрытия кнопки.

   [[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];

Ответ 7

В Swift:

self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)

Ответ 8

Попробуйте это.

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];    
[tvc release];

Ответ 9

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

extension UIViewController {

    func dismissAll(animated: Bool, completion: (() -> Void)? = nil) {
        if let optionalWindow = UIApplication.shared.delegate?.window, let window = optionalWindow, let rootViewController = window.rootViewController, let presentedViewController = rootViewController.presentedViewController  {
            if let snapshotView = window.snapshotView(afterScreenUpdates: false) {
                presentedViewController.view.addSubview(snapshotView)
                presentedViewController.modalTransitionStyle = .coverVertical
            }
            if !isBeingDismissed {
                rootViewController.dismiss(animated: animated, completion: completion)
            }
        }
    }

}

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

@IBAction func close() {
    dismissAll(animated: true)
}

Ответ 10

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

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

NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];

Ответ 11

  id vc = [self presentingViewController];
  id lastVC = self;
  while (vc != nil) {
    id tmp = vc;
    vc = [vc presentingViewController];
    lastVC = tmp;
  }
  [lastVC dismissViewControllerAnimated:YES completion:^{
}];

Ответ 12

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

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

Ответ 13

Вот решение, которое я использую, чтобы всплывать и отклонять все контроллеры представлений, чтобы вернуться к контроллеру корневого представления. У меня есть два метода в категории 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];

Ответ 14

Быстрая версия с некоторыми дополнениями на основе этого комментария

func dismissModalStack(viewController: UIViewController, animated: Bool, completionBlock: BasicBlock?) {
    if viewController.presentingViewController != nil {
        var vc = viewController.presentingViewController!
        while (vc.presentingViewController != nil) {
            vc = vc.presentingViewController!;
        }
        vc.dismissViewControllerAnimated(animated, completion: nil)

        if let c = completionBlock {
            c()
        }
    }
}

Ответ 15

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

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()

Ответ 16

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

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.

Ответ 17

Расширение Swift 3, основанное на приведенных выше ответах.

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

  • Сделайте снимок D
  • Добавьте этот снимок на B
  • Отключение от B без анимации
  • По завершении отпустите из A с анимацией

    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)
                })
            }
        }
    }
    

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

Ответ 18

Для Swift 3.0 +

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

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

Ответ 19

Отбросьте верхний VC анимированный, а другие нет. Если у вас три модальных VC

[self dismissModalViewControllerAnimated:NO]; // First
[self dismissModalViewControllerAnimated:NO]; // Second
[self dismissModalViewControllerAnimated:YES]; // Third

EDIT: если вы хотите сделать это только одним способом, сохраните иерархию в массиве VC и отпустите последний объект, а не другие.

Ответ 20

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

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

any intermediate view controllers are simply removed from the stack.

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

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

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

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

Ответ 21

В swift 4 и Xcode 9 это поможет вам.

var vc : UIViewController = self.presentingViewController!
        while ((vc.presentingViewController) != nil) {
            vc = vc.presentingViewController!
        }
        vc.dismiss(animated: true, completion: nil)

Наслаждаться !!! :)

Ответ 22

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