Comment remplacer le contrôleur de vue (ou supprimer de la pile de navigation)?

j'ai une petite application iPhone , qui utilise un contrôleur de navigation pour afficher 3 vues (ici fullscreen ):

Xcode screenshot

tout d'abord, il affiche une liste des réseaux sociaux (Facebook, Google+, etc.):

list screenshot

ensuite, il affiche un dialogue OAuth demandant des références:

login screenshot

et (après cela, dans le même UIWebView ) pour les permissions:

permissions screenshot

enfin il affiche le dernier contrôleur de vue avec les détails de l'utilisateur (dans l'application réelle ce sera le menu, où le jeu multijoueur peut être commencé):

details screenshot

tout Cela fonctionne bien, mais j'ai un problème, lorsque l'utilisateur souhaite revenir en arrière et sélectionner un autre réseau social:

l'utilisateur touche le bouton de retour et au lieu d'être affiché la première vue, est affiché la deuxième, en demandant des références/permissions de nouveau.

Que puis-je faire ici? Xcode 5.0.2 montre un choix très limité pour les segments - push , modal (que je ne peux pas utiliser, car il obscurcit la barre de navigation nécessaire pour mon jeu) et custom .

je suis un internaute novice de la programmation iOS, mais plus tôt j'ai développé un Adobe AIR mobile app et là il était possible de 1) Remplacer vue au lieu de pousser et 2) supprimer une vue inutile de la pile de navigation.

Comment faire la même chose dans une application native s'il vous plaît?

54
demandé sur Alexander Farber 2014-01-28 23:19:07

13 réponses

vous pouvez utiliser une séquence personnalisée: pour ce faire, vous avez besoin de créer une classe subclassing UIStoryboardSegue (exemple MyCustomSegue), et ensuite vous pouvez outrepasser le "perform" avec quelque chose comme ceci

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

à ce point aller à Interface Builder, sélectionnez "custom" suivre, et mettre le nom de votre classe (exemple MyCustomSegue)

37
répondu Daniele 2016-04-07 09:55:56

Pour développer les différents enchaîne ci-dessus, c'est ma solution. Il présente les avantages suivants:

  • peut fonctionner n'importe où dans la pile de vue, pas seulement la vue du Haut (pas sûr si cela est réellement nécessaire ou même techniquement possible de déclencher, mais il est là).
  • il ne provoque pas de pop ou de transition vers le contrôleur de vue précédent avant d'afficher le remplacement, il affiche juste le nouveau contrôleur avec un naturel transition, avec la navigation arrière étant à la même navigation arrière du contrôleur source.

Code De Séquence:

- (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];
}
55
répondu ima747 2014-10-21 03:37:18

la séquence personnalisée n'a pas fonctionné pour moi, car j'avais un contrôleur de vue Splash et je voulais le remplacer. Comme il n'y avait qu'un contrôleur de vue dans la liste, le popToRootViewController a quand même laissé le Splash sur la pile. J'ai utilisé le code suivant pour remplacer le contrôleur unique

-(void)perform {
    UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
    UIViewController *destinationController = (UIViewController*)[self destinationViewController];
    UINavigationController *navigationController = sourceViewController.navigationController;
    [navigationController setViewControllers:@[destinationController] animated:YES];
}

et maintenant dans Swift 4:

class ReplaceSegue: UIStoryboardSegue {

    override func perform() {
        source.navigationController?.setViewControllers([destination], animated: true)
    }
}

et maintenant dans Swift 2.0

class ReplaceSegue: UIStoryboardSegue {

    override func perform() {
        sourceViewController.navigationController?.setViewControllers([destinationViewController], animated: true)
    }
}
20
répondu christophercotton 2018-02-21 17:09:04

la version swift 2 de ima747 réponse:

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

comme il l'a mentionné, il a les avantages suivants:

  • peut fonctionner n'importe où dans la pile de vue, pas seulement la vue du Haut (pas sûr si cela est réellement nécessaire ou même techniquement possible de déclencher, mais il est là).
  • il ne provoque pas de pop ou de transition vers le contrôleur de vue précédent avant d'afficher le remplacement, il suffit affiche le nouveau contrôleur avec une transition naturelle, avec la navigation arrière étant à la même navigation arrière du contrôleur source.
16
répondu Dr.Agos 2015-10-26 23:36:28

pour ce problème, je pense que la réponse est simple comme

  1. Obtenir la matrice de vue des contrôleurs de NavigationController
  2. suppression du dernier ViewController (current view controller)
  3. insérer enfin un nouveau
  4. puis remettre le tableau des ViewControllers sur le navigationController comme suit:

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

fonctionne parfaitement avec Swift 3.

J'espère que ça aidera les nouveaux.

santé.

12
répondu Xuan-Gieng Nguyen 2017-06-15 17:12:00

utilisant unwind segue serait la solution la plus appropriée à ce problème. Je suis D'accord avec Lauro.

Voici une brève explication pour configurer une séquence de décompression de detailsViewController[ou viewController3] vers myAuthViewController [ou viewController1]

c'est essentiellement la façon dont vous procéderiez pour effectuer une séquence de décompression à travers le code.

  • viewController que vous voulez décompresser(dans ce cas, viewController1). Le nom de la méthode peut être n'importe quoi si long qu'il prend un argument du type UIStoryboardSegue.

    @IBAction func unwindToMyAuth(segue: UIStoryboardSegue) {
    println("segue with ID: %@", segue.Identifier)
    }
    
  • Lien de cette méthode dans le viewController(3) vous voulez vous détendre. Pour lier, clic droit (double claquement de doigt) sur l'icône de sortie en haut du viewController, à ce point la méthode 'unwindToMyAuth' s'affichera dans la boîte pop up. Contrôle cliquez de cette méthode à la première icône, l'icône viewController (également présente en haut du viewController, dans la même ligne que l'icône exit). Sélectionnez l'option' Manuel ' qui apparaît.

  • dans le schéma du Document, pour la même vue(viewController3), sélectionnez la séquence déroulante que vous venez de créer. Aller à l'inspecteur attribué et assigner un identifiant unique pour cette séquence de décompression. Nous avons maintenant une séquence de décompression Générique prête à être utilisée.

  • maintenant, la séquence de décompression peut être exécutée comme n'importe quelle autre séquence du code.

    performSegueWithIdentifier("unwind.to.myauth", sender: nil)
    

cette approche, vous mènera de viewController3 à viewController1 sans avoir à supprimer viewController2 de la hiérarchie de navigation.

contrairement à d'autres segues, unwind segues n'instancie pas un contrôleur de vue, ils vont seulement à un contrôleur de vue existant dans la hiérarchie de navigation.

9
répondu Lester 2015-01-10 18:24:24

comme mentionné dans les réponses précédentes à pop pas animé et puis de pousser animé ne sera pas très bon parce que l'utilisateur verra le processus réel. Je vous recommande d'abord de pousser animé et ensuite supprimer le précédent cr. Comme ceci:

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)
        }
    }
}
4
répondu Alex Shubin 2018-01-25 06:53:55

utiliser le code ci-dessous last view controller Vous pouvez utiliser un autre bouton ou le mettre vous-même à la place du bouton d'Annulation que j'ai utilisé

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

}
2
répondu bhavya kothari 2014-01-29 10:17:54

dans swift3 - ajouter un identifiant - ajouter et mettre en segue (storyboard) classe de storyboard personnalisé à partir de fichier cocoatouch - Dans la classe custom 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)
    }
    }

- vous devez aussi remplacer une méthode dans votre viewcontroller source

  override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    return false
}
2
répondu Mahesh Giri 2016-11-17 07:36:04

Eh bien, ce que vous pouvez également faire est d'utiliser le dispositif de controller unwind view.

en Fait, je pense que c'est exactement ce dont vous avez besoin.

vérifier cette entrée: Qu'est-ce qu'Unwind segues for et comment les utilisez-vous?

1
répondu Lauro Wolff Valente Sobrinho 2017-05-23 12:02:06

ce que vous devriez vraiment faire est de présenter modalement un UINavigationController contenant le réseau social UIViewControllers overtop de votre Menu UIViewController (qui peut être intégré dans un UINavigationController si vous voulez). Puis, une fois qu'un utilisateur a authentifié, vous rejetez le réseau social UINavigationController , montrant à nouveau votre Menu UIViewController .

1
répondu Mark 2014-11-13 19:34:01

cela a fonctionné pour moi dans Swift 3:

class ReplaceSegue: UIStoryboardSegue {
    override func perform() {
        if let navVC = source.navigationController {
            navVC.pushViewController(destination, animated: true)
        } else {
            super.perform()
        }
    }
}
1
répondu jeffbailey 2016-10-11 01:15:10

Comment à propos de ceci:) Je maintenant c'est une vieille question, mais cela va fonctionner comme un charme:

UIViewController *destinationController = [[UIViewController alloc] init];
UINavigationController *newNavigation = [[UINavigationController alloc] init];
[newNavigation setViewControllers:@[destinationController]];
[[[UIApplication sharedApplication] delegate] window].rootViewController = newNavigation;
1
répondu Denis Kozhukhov 2018-06-21 01:01:11