Вместо push segue как заменить контроллер вида (или удалить из навигационного стека)?
У меня есть небольшое приложение для iPhone, который использует навигационный контроллер для отображения 3 видов (здесь полноэкранный режим):

сначала он отображает список социальных сетей (Facebook, Google+ и др.):

затем он отображает диалоговое окно OAuth с запросом учетных данных:

и (после этого, в том же UIWebView) для разрешения:

наконец он отображает последний вид контроллера с пользовательскими данными (в реальном приложении это будет меню, где можно запустить многопользовательскую игру):

все это работает хорошо, но у меня есть проблема, когда пользователь хочет вернуться и выбрать другую социальную сеть:
пользователь нажимает кнопку "назад" и вместо отображения первого представления отображается второй, снова запрашивая учетные данные/разрешения OAuth.
что я могу сделать здесь? В Xcode 5.0.2 показывает очень ограниченный выбор для связок - push,модальные (который я не могу использовать, потому что он скрывает панель навигации нужен для моей игры) и custom.
Я новичок в программировании iOS, но ранее я разработал Adobe AIR mobile app и там можно было 1) заменить вид вместо нажатия и 2) удалить ненужный вид из навигационного стека.
Как сделать то же самое в родном приложение, пожалуйста?
13 ответов:
вы можете использовать пользовательский сегмент: для этого вам нужно создать подкласс класса UIStoryboardSegue (пример MyCustomSegue), а затем вы можете переопределить "выполнить" с чем-то вроде этого
-(void)perform { UIViewController *sourceViewController = (UIViewController*)[self sourceViewController]; UIViewController *destinationController = (UIViewController*)[self destinationViewController]; UINavigationController *navigationController = sourceViewController.navigationController; // Pop to root view controller (not animated) before pushing [navigationController popToRootViewControllerAnimated:NO]; [navigationController pushViewController:destinationController animated:YES]; }на этом этапе перейдите в Interface Builder, выберите" custom " segue и введите имя вашего класса (пример MyCustomSegue)
чтобы расширить различные сегменты выше, это мое решение. Он имеет следующие преимущества:
- может работать в любом месте стека просмотра, а не только вид сверху (не уверен, что это реально когда-либо нужно или даже технически возможно запустить, но эй, это там).
- Он не вызывает поп или переход к предыдущему контроллеру вида перед отображением замены, он просто отображает новый контроллер с естественным переходом, с обратной стороны навигация к такой же задней навигации регулятора источника.
Segue Код:
- (void)perform { // Grab Variables for readability UIViewController *sourceViewController = (UIViewController*)[self sourceViewController]; UIViewController *destinationController = (UIViewController*)[self destinationViewController]; UINavigationController *navigationController = sourceViewController.navigationController; // Get a changeable copy of the stack NSMutableArray *controllerStack = [NSMutableArray arrayWithArray:navigationController.viewControllers]; // Replace the source controller with the destination controller, wherever the source may be [controllerStack replaceObjectAtIndex:[controllerStack indexOfObject:sourceViewController] withObject:destinationController]; // Assign the updated stack with animation [navigationController setViewControllers:controllerStack animated:YES]; }
пользовательский сегмент не работал для меня, так как у меня был контроллер Splash view, и я хотел его заменить. Поскольку в списке был только один контроллер вида,
popToRootViewControllerвсе еще оставил всплеск на стеке. Я использовал следующий код для замены одного контроллера-(void)perform { UIViewController *sourceViewController = (UIViewController*)[self sourceViewController]; UIViewController *destinationController = (UIViewController*)[self destinationViewController]; UINavigationController *navigationController = sourceViewController.navigationController; [navigationController setViewControllers:@[destinationController] animated:YES]; }а теперь в Swift 4:
class ReplaceSegue: UIStoryboardSegue { override func perform() { source.navigationController?.setViewControllers([destination], animated: true) } }а теперь в Swift 2.0
class ReplaceSegue: UIStoryboardSegue { override func perform() { sourceViewController.navigationController?.setViewControllers([destinationViewController], animated: true) } }
swift 2 версия ima747 ответ:
override func perform() { let navigationController: UINavigationController = sourceViewController.navigationController!; var controllerStack = navigationController.viewControllers; let index = controllerStack.indexOf(sourceViewController); controllerStack[index!] = destinationViewController navigationController.setViewControllers(controllerStack, animated: true); }как он уже упоминал, он имеет следующие преимущества:
- может работать в любом месте стека просмотра, а не только вид сверху (не уверен, что это реально когда-либо нужно или даже технически возможно запустить, но эй, это там).
- это не вызывает поп или переход к предыдущему контроллеру вида перед отображением замены, он просто отображает новый контроллер с естественным переход, с задней навигацией, являющейся той же самой задней навигацией исходного контроллера.
для этой проблемы, я думаю, что ответ просто, как
- получить массив контроллеров вида из NavigationController
- удаление последнего ViewController (текущий контроллер вида)
- наконец-то вставить новый
затем установите массив ViewControllers обратно в navigationController как ниже:
if let navController = self.navigationController { let newVC = DestinationViewController(nibName: "DestinationViewController", bundle: nil) var stack = navController.viewControllers stack.remove(at: stack.count - 1) // remove current VC stack.insert(newVC, at: stack.count) // add the new one navController.setViewControllers(stack, animated: true) // boom! }работает с Swift 3.
надеюсь, что это помогает некоторым новым парням.
Ура.
использование unwind segue было бы наиболее подходящим решением этой проблемы. Я согласен с Лауро.
вот краткое объяснение для настройки размотки сегмент от detailsViewController[или viewController3] к myAuthViewController[или viewController1]
по сути, это то, как вы будете выполнять сегмент разматывания через код.
реализовать метод IBAction в viewController, который вы хотите развернуть(в этом случае viewController1). Имя метода может быть любым настолько длинным, что оно принимает один аргумент типа UIStoryboardSegue.
@IBAction func unwindToMyAuth(segue: UIStoryboardSegue) { println("segue with ID: %@", segue.Identifier) }свяжите этот метод в viewController (3), от которого вы хотите избавиться. Чтобы связать, щелкните правой кнопкой мыши (двойной палец нажмите) на значок выхода в верхней части viewController, в этот момент 'unwindToMyAuth' метод будет отображаться во всплывающем окне. Нажмите кнопку управления от этого метода до первого значка, значок viewController(также присутствует в верхней части окна viewController, в той же строке, что и значок выхода). Выберите "ручной" вариант, который появляется.
в структуре документа для того же представления(viewController3) выберите только что созданный сегмент размотки. Перейдите к инспектору атрибутов и назначьте уникальный идентификатор для этого этапа размотки. Теперь у нас есть общий сегмент размотки, готовый к использованию.
теперь, размотать сегмент может быть выполнен так же, как и любой другой сегмент из кода.
performSegueWithIdentifier("unwind.to.myauth", sender: nil)этот подход приведет вас из viewController3 в viewController1 без необходимости удалять viewController2 из иерархии навигации.
В отличие от других сегментов, unwind segues не создают экземпляр контроллера вида, они только переходят к существующему контроллеру вида в иерархии навигации.
Как упоминалось в предыдущих ответах на pop not animated, а затем нажать animated не будет выглядеть очень хорошо, потому что пользователь увидит фактический процесс. Я рекомендую вам сначала нажать анимированный, а затем удалить предыдущий vc. Вот так:
extension UINavigationController { func replaceCurrentViewController(with viewController: UIViewController, animated: Bool) { pushViewController(viewController, animated: animated) let indexToRemove = viewControllers.count - 2 if indexToRemove >= 0 { viewControllers.remove(at: indexToRemove) } } }
использовать ниже код последнего контроллера вид Вы можете использовать другую кнопку или поставить его самостоятельно вместо кнопки отмены я использовал
- (void)viewDidLoad { [super viewDidLoad]; [self.navigationController setNavigationBarHidden:YES]; UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(dismiss:)]; self.navigationItemSetting.leftBarButtonItem = cancelButton; } - (IBAction)dismissSettings:(id)sender { // your logout code for social media selected [self.navigationController popToRootViewControllerAnimated:YES]; }
на
swift3создать один переход -добавить идентификатор - добавить и установить в segue(раскадровка) пользовательский класс раскадровки из файла cocoatouch - В пользовательском классе override perform ()override func perform() { let sourceViewController = self.source let destinationController = self.destination let navigationController = sourceViewController.navigationController // Pop to root view controller (not animated) before pushing if self.identifier == "your identifier"{ navigationController?.popViewController(animated: false) navigationController?.pushViewController(destinationController, animated: true) } }-вы также должны переопределить один метод в вашем исходном viewcontroller
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { return false }
Ну, то, что вы также можете сделать, это использовать размотать вид контроллера вещи.
на самом деле я думаю, что это именно то, что вам нужно.
проверить эту запись: Что такое раскрутка сегментов и как вы их используете?
что вы действительно должны сделать, это модально представить
UINavigationControllerсодержащий социальную сетьUIViewControllersповерх вашего менюUIViewController(который может быть внедрен вUINavigationControllerЕсли вы хотите). Затем, как только пользователь прошел проверку подлинности, вы закрываете социальную сетьUINavigationController, показывая свое менюUIViewControllerснова.
это сработало для меня в Swift 3:
class ReplaceSegue: UIStoryboardSegue { override func perform() { if let navVC = source.navigationController { navVC.pushViewController(destination, animated: true) } else { super.perform() } } }
Как насчет этого :) я теперь это старый вопрос, но это будет работать как шарм:
UIViewController *destinationController = [[UIViewController alloc] init]; UINavigationController *newNavigation = [[UINavigationController alloc] init]; [newNavigation setViewControllers:@[destinationController]]; [[[UIApplication sharedApplication] delegate] window].rootViewController = newNavigation;
Comments