iPhone - уволить несколько просмотра концентраторов

StackOverflow https://stackoverflow.com/questions/2944191

Вопрос

У меня есть иерархия контроллеров длинного просмотра;

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

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

и так далее.

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

[self dismissModalViewControllerAnimated:YES];

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

Спасибо

Это было полезно?

Решение 2

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

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

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

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

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

вернуться к моему дому.

Другие советы

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

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

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

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

Объектив-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];
}

Быстрый

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; доктор

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

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

например root a -> подарок b -> представляет c И вы хотите увольнять к А от C, вы можете официально позвонить dismissViewControllerAnimated на rootViewController.

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

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


так

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

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

В 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, а первый контроллер представления установлен в качестве контроллера корневого просмотра.

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

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

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

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

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

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

2. Промедление Уволяние функции увольнения в RootViewController

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

3.Нотификация Опубликовать каждый закрытый или отклоненный событие кнопки.

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

В SWIFT:

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

Проблема с большинством решений заключается в том, что при отклонении стопки представленных просмотра контроллеров пользователь кратко увидит первый представленный ViewConroller в стеке, как оно уволен. Отличное решение 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)
}

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

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

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

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

NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
  id vc = [self presentingViewController];
  id lastVC = self;
  while (vc != nil) {
    id tmp = vc;
    vc = [vc presentingViewController];
    lastVC = tmp;
  }
  [lastVC dismissViewControllerAnimated:YES completion:^{
}];

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

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

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

Простой рекурсивный ближе:

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

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

Маленький мерцающий на симуляторе, но не на устройстве.

Свидное расширение на основе вышеуказанных ответов:

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 на том.

Для Swift 3.0+

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

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

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

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

Уволить лучший VC анимированные и другие не. Если вы достойны трех модальных VC

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

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

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

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

any intermediate view controllers are simply removed from the stack.

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

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

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

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

В Swift 4. А также XCode 9. Это поможет вам.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top