Вместо push segue как заменить контроллер вида (или удалить из навигационного стека)?



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



Xcode screenshot



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



list screenshot



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



login screenshot



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



permissions screenshot



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



details screenshot



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



пользователь нажимает кнопку "назад" и вместо отображения первого представления отображается второй, снова запрашивая учетные данные/разрешения OAuth.



что я могу сделать здесь? В Xcode 5.0.2 показывает очень ограниченный выбор для связок - push,модальные (который я не могу использовать, потому что он скрывает панель навигации нужен для моей игры) и custom.



Я новичок в программировании iOS, но ранее я разработал Adobe AIR mobile app и там можно было 1) заменить вид вместо нажатия и 2) удалить ненужный вид из навигационного стека.



Как сделать то же самое в родном приложение, пожалуйста?

642   13  

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

как он уже упоминал, он имеет следующие преимущества:

  • может работать в любом месте стека просмотра, а не только вид сверху (не уверен, что это реально когда-либо нужно или даже технически возможно запустить, но эй, это там).
  • это не вызывает поп или переход к предыдущему контроллеру вида перед отображением замены, он просто отображает новый контроллер с естественным переход, с задней навигацией, являющейся той же самой задней навигацией исходного контроллера.

для этой проблемы, я думаю, что ответ просто, как

  1. получить массив контроллеров вида из NavigationController
  2. удаление последнего ViewController (текущий контроллер вида)
  3. наконец-то вставить новый
  4. затем установите массив 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

    Ничего не найдено.