iPhone – отклонение нескольких ViewControllers

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

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

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

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

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

и так далее.

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

 [self dismissModalViewControllerAnimated:YES]; 

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

благодаря

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

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

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

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

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

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

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

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

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

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

в 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:^{}]; } 

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

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

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

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

Универсальный метод 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]; } 

стриж

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

ТЛ; др

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

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

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

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

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


так

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

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

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

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

1.Register Уведомление в 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.Notification post при каждом закрытии или закрытии кнопки.

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

В Свифт:

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

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

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

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

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

 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:^{ }]; 

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

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

Пожалуйста, используйте это. Счастливое кодирование 🙂

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

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

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

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

Вызов

 baseController.dismissEntireStackAndSelf() 

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

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

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

 extension UIViewController { func dismissUntilAnimated(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(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) } } } 

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

Для Swift 3.0+

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

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

Проблема большинства решений заключается в том, что, когда вы отклоняете стек представленных viewControllers, пользователь вкратце увидит первый представленный 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 didTouchDoneButton() { dismissAll(animated: true) } 

Отключите верхнюю VC анимацию, а другие нет. Если у вас три модальных VC

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

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

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

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

 any intermediate view controllers are simply removed from the stack. 

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

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

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

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

В быстрых 4 и Xcode 9 Это поможет вам.

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

Наслаждаться !!! 🙂

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

Interesting Posts

Когда следует использовать ключевое слово “strictfp” в java?

Как улучшить работу графической карты ATI?

Почему существует разница между ping «localhost» и ping «локальный IP-адрес»?

Android Studio: Не удалось синхронизировать Gradle: не удалось HEAD ‘…’. Получен код статуса 502 с сервера: Bad Gateway

Диалоговое окно «Открыть запуск …» из команды

Сделать изменение языка специфичным для окна / приложения не всей системы (Windows 8.1)

Неопределенная ссылка на статическую переменную c ++

Должен ли я использовать int или Int32

Android Как нарисовать гладкую линию после пальца

Как управлять версией записи в базе данных

Подключение к моей сети через внешний IP-адрес

Что такое mock-объекты в Java?

Редактирование видео в командной строке в Linux (вырезание, подключение и предварительный просмотр)

Адрес конечной точки изменения WCF во время выполнения

Простые логические операторы в Bash

Давайте будем гением компьютера.