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 ]

62 голосов
/ 07 января 2015

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

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

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

24 голосов
/ 02 июня 2010

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

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

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

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

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

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

15 голосов
/ 03 июня 2016

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

ТЛ; др

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

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

например. root A -> Presents B -> Presents C , и вы хотите отстранить A от C, вы можете официально позвонить по номеру dismissViewControllerAnimated на rootViewController.

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

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


так

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

14 голосов
/ 31 августа 2013

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

в 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 голосов
/ 01 марта 2013
[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];

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

3 голосов
/ 26 марта 2015

В Свифт:

self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
3 голосов
/ 23 декабря 2017

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

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)
}
3 голосов
/ 28 февраля 2014

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

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

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

2. Реализация функции dismissModelViewController в rootviewController

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

3. Уведомление о каждом событии кнопки закрытия или закрытия.

   [[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];
2 голосов
/ 27 ноября 2012

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

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];    
[tvc release];
1 голос
/ 02 ноября 2015

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

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