Подтвердить что ты не робот

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

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

Xcode screenshot

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

list screenshot

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

login screenshot

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

permissions screenshot

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

details screenshot

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

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

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

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

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

4b9b3361

Ответ 1

Вы можете использовать пользовательский segue: для этого вам нужно создать подкласс класса 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)

Ответ 2

Чтобы развернуть на разных уровнях выше, это мое решение. Он имеет следующие преимущества:

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

Код 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];
}

Ответ 3

Пользовательский переход не работал для меня, так как у меня был контроллер представления Splash, и я хотел заменить его. Поскольку в списке был только один контроллер представления, popToRootViewController прежнему оставлял Splash в стеке. Я использовал следующий код для замены одного контроллера

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

Ответ 4

оперативная версия 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);
}

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

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

Ответ 5

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

  • Получить массив контроллеров представлений из 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.

Надеюсь, это поможет некоторым новым парням.

Приветствия.

Ответ 6

Использование разматывания segue было бы наиболее подходящим решением этой проблемы. Я согласен с Лауро.

Ниже приведено краткое объяснение для настройки размотки segue из detailsViewController [или viewController3] в myAuthViewController [или viewController1]

Это, по сути, то, как вы могли бы выполнять размотку segue через код.

  • Внедрите метод 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 из иерархии навигации.

В отличие от других segues, unind segues не создают экземпляр контроллера представления, они переходят только к существующему контроллеру представления в иерархии навигации.

Ответ 7

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

Ответ 8

Использовать нижестоящий контроллер последнего кода Вы можете использовать другую кнопку или добавить ее вместо кнопки отмены, которую я использовал

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

}

Ответ 9

То, что вы действительно должны сделать, это модально представить UINavigationController, содержащую социальную сеть UIViewControllers, верхнюю часть вашего меню UIViewController (которая может быть встроена в UINavigationController, если вы хотите). Затем, после аутентификации пользователя, вы отключите социальную сеть UINavigationController, снова показывая свое меню UIViewController.

Ответ 10

В swift3 создать один сегмент идентификатор -add -add и установить в segue (раскадровки) пользовательский класс раскадровки из файла cocoatouch -Интерфейс переопределения класса()

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

-Вы также должны переопределить один метод в вашем исходном представлении управления

  override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    return false
}

Ответ 12

Это работало для меня в Swift 3:

class ReplaceSegue: UIStoryboardSegue {
    override func perform() {
        if let navVC = source.navigationController {
            navVC.pushViewController(destination, animated: true)
        } else {
            super.perform()
        }
    }
}

Ответ 13

Как насчет этого :) У меня сейчас старый вопрос, но это будет работать как прелесть

UIViewController *destinationController = [[UIViewController alloc] init];
UINavigationController *newNavigation = [[UINavigationController alloc] init];
[newNavigation setViewControllers:@[destinationController]];
[[[UIApplication sharedApplication] delegate] window].rootViewController = newNavigation;

Ответ 14

Вот быстрое решение Swift 4/5 путем создания пользовательского эффекта, который заменяет (верхний стек) viewcontroller новым (без анимации):

class SegueNavigationReplaceTop: UIStoryboardSegue {

    override func perform () {
        guard let navigationController = source.navigationController else { return }
        navigationController.popViewController(animated: false)
        navigationController.pushViewController(destination, animated: false)
    }
}