Création d'une séquence par programmation

202

J'ai un point commun UIViewControllerqui me permet UIViewsControllersde réutiliser certaines opérations courantes.

Je veux mettre en place un enchaînement sur ce "commun" UIViewControllerpour que tous les autres UIViewControllershéritent.

J'essaie de comprendre comment je fais cela par programme.

Je suppose que la question pourrait également être de savoir comment définir un seguepour tous mes UIViewControllerssans entrer dans le story-board et les faire à la main.

Tiago Veloso
la source

Réponses:

169

Par définition, une séquence ne peut pas vraiment exister indépendamment d'un storyboard. Il est même là au nom de la classe: UIStoryboardSegue. Vous ne créez pas de séquences par programmation - c'est le runtime du storyboard qui les crée pour vous. Vous pouvez normalement appeler performSegueWithIdentifier:le code de votre contrôleur de vue, mais cela repose sur le fait d'avoir une séquence déjà configurée dans le storyboard pour référence.

Je pense cependant que vous demandez comment créer une méthode dans votre contrôleur de vue commun (classe de base) qui passera à un nouveau contrôleur de vue et sera héritée par toutes les classes dérivées. Vous pouvez le faire en créant une méthode comme celle-ci sur votre contrôleur de vue de classe de base:

- (IBAction)pushMyNewViewController
{
    MyNewViewController *myNewVC = [[MyNewViewController alloc] init];

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

puis dans votre classe dérivée, appelez cette méthode lorsque le bouton approprié est cliqué ou que la ligne du tableau est sélectionnée ou autre.

jonkroll
la source
4
Merci. C'est dommage que nous ne puissions pas le faire par programme. Cela augmenterait vraiment la qualité du code source (moins de duplication est toujours bonne). Je vais essayer votre suggestion.
Tiago Veloso
2
@jonkroll est-il possible d'appeler / effectuer une séquence à partir d'une instruction switch, c'est-à-dire en fonction de l'index que j'ai?
codejunkie
5
@codejunkie: Oui, vous pouvez le faire. Vous utiliseriez la UIViewControllerméthode nommée performSegueWithIdentifier:sender:pour cela.
jonkroll le
2
J'ai créé et effectué un enchaînement par programme (voir ma réponse). Quelque chose ne va pas avec mon code, alors, si votre réponse est correcte?
Jean-Philippe Pellet,
14
Mise à jour pour iOS 6+: celle UIView-ci presentModalViewController:animated:est déconseillée. De la documentation - (obsolète dans iOS 6.0. Utilisez presentViewController: animated: complétion: à la place.)
utilisateur
346

J'ai pensé ajouter une autre possibilité. L'une des choses que vous pouvez faire est de connecter deux scènes dans un storyboard à l'aide d'une séquence qui n'est pas attachée à une action, puis de déclencher la séquence par programmation dans votre contrôleur de vue. Pour ce faire, vous devez faire glisser depuis l'icône du propriétaire du fichier en bas de la scène du storyboard qui est la scène de transition, et faire glisser vers la droite vers la scène de destination. Je vais jeter une image pour aider à expliquer.

entrez la description de l'image ici

Une fenêtre apparaîtra pour "Manual Segue". J'ai choisi Push comme type. Appuyez sur le petit carré et assurez-vous que vous êtes dans l'inspecteur d'attributs. Donnez-lui un identifiant que vous utiliserez pour y faire référence dans le code.

entrez la description de l'image ici

Ok, je vais ensuite utiliser un élément de bouton de barre de programmation. Dans viewDidLoad ou ailleurs, je vais créer un élément de bouton dans la barre de navigation avec ce code:

UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:@"Buttonize"
                                                                    style:UIBarButtonItemStyleDone
                                                                   target:self
                                                                   action:@selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = @[buttonizeButton];

Ok, notez que le sélecteur est buttonizeButtonTap :. Donc, écrivez une méthode void pour ce bouton et dans cette méthode, vous appellerez la séquence comme ceci:

-(void)buttonizeButtonTap:(id)sender{
    [self performSegueWithIdentifier:@"Associate" sender:sender];
    }

Le paramètre sender est requis pour identifier le bouton lorsque prepareForSegue est appelé. prepareForSegue est la méthode de framework où vous allez instancier votre scène et lui passer toutes les valeurs dont elle aura besoin pour faire son travail. Voici à quoi ressemble ma méthode:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"Associate"])
    {
        TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
        translationQuizAssociateVC.contentID = self.contentID;
        translationQuizAssociateVC.index = self.index;
        translationQuizAssociateVC.content = self.content;
    }
}

Ok, viens de le tester et ça marche. J'espère que cela vous aide.

smileBot
la source
@MichaelRowe comment cela élimine-t-il le besoin de séquences? Comme je le vois, vous devez toujours faire glisser et déposer sur Storyboard vers le contrôleur de destination ..
aherrick
@MichaelRowe, cela n'élimine pas le besoin de séquences. Cela vous permet de faire la transition entre les contrôleurs de vue qui sont construits dans du code plutôt que dans le générateur d'interface.
Matthew
@Matt, il me suffit de repenser complètement la façon dont j'ai configuré mon application ... Après une réécriture complète de toute l'interface utilisateur, je n'utilise plus de séquences ..
Michael Rowe
@cocoanut j'obtiens l'erreur comme "L'application a essayé de présenter modalement un contrôleur actif" toute aide à ce sujet ..
Bala
1
Manual Segue "Push" est déconseillé, utilisez "Show". Cette réponse contient plus de détails. @smileBot veuillez mettre à jour la réponse.
NAbbas
81

J'ai utilisé ce code pour instancier ma sous-classe de séquence personnalisée et l'exécuter par programme. Cela semble fonctionner. Quelque chose ne va pas avec ça? Je suis perplexe, lisant toutes les autres réponses disant que cela ne peut pas être fait.

UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:@"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];
Jean-Philippe Pellet
la source
4
Que contient MyCustomSegue?
Victor Engel
3
Il s'agit d'une sous-classe personnalisée de UIStoryboardSegue.
Jean-Philippe Pellet
7
@MarkAmery Beaucoup de gens (dont moi) évitent d'utiliser des storyboards. Ils sont difficiles à fusionner et il n'y a pas de vérification au moment de la compilation que l'ID auquel je passe performSegueWithIdentifier:est vraiment défini dans le storyboard. J'évite tous les problèmes si je crée moi-même la séquence.
Jean-Philippe Pellet
3
Je suis d'accord avec Jean-Philippe. La gestion du storyboard est une douleur dans le cul. Bien sûr, il est facile de cliquer sur votre chemin en créant quelques vues et en ajoutant un enchaînement ici et un enchaînement là, mais gérer 6 vues avec 16 séquences définies en XML, lorsque vous avez trois développeurs tous en train de bidouiller, c'est terrible. Quoi qu'il en soit, le fait est que: le code vous donne le contrôle, le xml généré par xcode ne le fait pas.
Krystian
3
Je vois un crash dans [segue perform] dans iOS7, je ne sais pas si quelqu'un d'autre en fait l'expérience.
Eric Chen
45

Je suppose que c'est répondu et accepté, mais je voudrais juste ajouter quelques détails supplémentaires.

Ce que j'ai fait pour résoudre un problème où je présenterais une vue de connexion comme premier écran et que je voulais ensuite accéder à l'application si la connexion était correcte. J'ai créé la séquence à partir du contrôleur de connexion-vue vers le contrôleur de vue racine et lui ai donné un identifiant comme "myidentifier".

Ensuite, après avoir vérifié tous les codes de connexion si la connexion était correcte, j'appellerais

[self performSegueWithIdentifier: @"myidentifier" sender: self];

Mon plus grand malentendu était que j'ai essayé de mettre la séquence sur un bouton et d'interrompre la séquence une fois qu'elle a été trouvée.

qrikko
la source
4
Comme je l'ai écrit comme un autre commentaire: j'ai créé et effectué des séquences personnalisées par programme (voir ma réponse).
Jean-Philippe Pellet
32

Vous devez lier votre code à celui UIStoryboardque vous utilisez. Assurez-vous d'entrer dans YourViewController dans votre UIStoryboard, cliquez sur la bordure qui l'entoure, puis définissez son identifierchamp sur un NSStringque vous appelez dans votre code.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];
Jeff Grimes
la source
1
J'obtiens ceci, mais que se passe-t-il si le viewController que je veux présenter est intégré dans un NavigationController dans le storyboard? D'après ce que je peux trouver, je peux initialiser un NavigationController pour l'intégrer, mais dans le storyboard, j'ai déjà configuré les séquences de push pour la vue qui doit être présentée.
jhilgert00
1
Pouvez-vous élaborer sur ce sujet? Je pense que c'est le problème que j'ai, mais je n'arrive pas à trouver comment / où le faire ...
jesses.co.tt
1
Même cette solution est correcte, il s'agit d'éviter toute enchaînement, mais la question concerne l'enchaînement. De cette façon, vous pouvez vous connecter ou faire une transition entre deux scènes SANS enchaînement dans les storyboards.
BootMaker
16

Pour les contrôleurs qui sont dans le storyboard.

jhilgert00 est-ce ce que vous cherchez?

-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:@"HomeController"];
[self.navigationController pushViewController: myController animated:YES];

}

OU...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];
user1723341
la source
3

Je voudrais ajouter une précision ...

Un malentendu commun, en fait que j'ai eu pendant un certain temps, est qu'une séquence de storyboard est déclenchée par la prepareForSegue:sender:méthode. Ce n'est pas. Une séquence de storyboard sera exécutée, que vous ayez ou non implémenté une prepareForSegue:sender:méthode pour ce contrôleur de vue (en partant).

J'ai appris cela des excellentes conférences iTunesU de Paul Hegarty . Mes excuses, mais je ne me souviens malheureusement pas de quelle conférence.

Si vous connectez une séquence entre deux contrôleurs de vue dans un storyboard, mais que vous n'implémentez pas de prepareForSegue:sender:méthode, la séquence se connecte toujours au contrôleur de vue cible. Il se connecte cependant à ce contrôleur de vue sans préparation.

J'espère que cela t'aides.

andrewbuilder
la source
3

Les séquences de storyboard ne doivent pas être créées en dehors du storyboard. Vous devrez le câbler, malgré les inconvénients.

La référence UIStoryboardSegue indique clairement:

Vous ne créez pas directement d'objets de transition. Au lieu de cela, le runboard du storyboard les crée lorsqu'il doit effectuer une transition entre deux contrôleurs de vue. Vous pouvez toujours initier un enchaînement par programme en utilisant la méthode performSegueWithIdentifier: sender: de UIViewController si vous le souhaitez. Vous pouvez le faire pour lancer une séquence à partir d'une source qui a été ajoutée par programme et qui n'est donc pas disponible dans Interface Builder.

Vous pouvez toujours demander par programmation au storyboard de présenter un contrôleur de vue à l'aide d'une séquence de transition presentModalViewController:ou d' pushViewController:animated:appels, mais vous aurez besoin d'une instance de storyboard.

Vous pouvez appeler la UIStoryboardméthode de classe s pour obtenir un storyboard nommé avec bundle nil pour le bundle principal.

storyboardWithName:bundle:

Cameron Lowell Palmer
la source
2

Tout d'abord, supposons que vous ayez deux vues différentes dans le storyboard et que vous souhaitiez naviguer d'un écran à un autre, procédez comme suit:

1). Définissez toutes vos vues avec le fichier de classe et également l'ID du storyboard dans l'inspecteur d'identité.

2). Assurez-vous d'ajouter un contrôleur de navigation à la première vue. Sélectionnez-le dans le Storyboard puis Editeur> Incorporer dans> Contrôleur de navigation

3). Dans votre première classe, importez le "secondClass.h"

#import "ViewController.h
#import "secondController.h"

4). Ajoutez cette commande dans l'IBAction qui doit effectuer la séquence

secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
[self.navigationController pushViewController:next animated:YES];

5). @"second"est la classe du contrôleur secondview, id du storyboard.

Sanket Chauhan
la source
self.storyboarddevrait être:UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
masipcat
@masipcat et le nom du tableau d'histoires peuvent dépendre de la façon dont vous configurez votre projet Xcode, dans le mien c'était "Main.storyboard" donc j'ai utiliséstoryboardWithName:@"Main"
ammianus
@ sanket-chauhan si votre premier contrôleur n'est pas intégré dans un contrôleur de navigation, vous pouvez également afficher la vue suivante en utilisant [self showDetailViewController:next sender:self];ou[self showViewController:next sender:self];
ammianus
1

J'ai procédé à une ingénierie inverse et effectué une (ré) implémentation open source des séquences d'UIStoryboard: https://github.com/acoomans/Segway

Avec cette bibliothèque, vous pouvez définir des séquences par programmation (sans aucun storyboard).

J'espère que cela peut aider.

acoomans
la source
0

Quelques problèmes, en fait:

Tout d'abord, dans ce projet que vous avez téléchargé pour nous, la séquence ne porte pas l'identifiant "segue1":

pas d'identifiant

Vous devez remplir cet identifiant si vous ne l'avez pas déjà fait.

Deuxièmement, lorsque vous passez d'une vue de table à une vue de table, vous appelez initWithNibName pour créer un contrôleur de vue. Vous voulez vraiment utiliser instantiateViewControllerWithIdentifier.

Jaydip
la source
0

Voici l'exemple de code pour Creating a segue programmatically:

class ViewController: UIViewController {
    ...
    // 1. Define the Segue
    private var commonSegue: UIStoryboardSegue!
    ...
    override func viewDidLoad() {
        ...
        // 2. Initialize the Segue
        self.commonSegue = UIStoryboardSegue(identifier: "CommonSegue", source: ..., destination: ...) {
            self.commonSegue.source.showDetailViewController(self.commonSegue.destination, sender: self)
        }
        ...
    }
    ...
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 4. Prepare to perform the Segue
        if self.commonSegue == segue {
            ...
        }
        ...
    }
    ...
    func actionFunction() {
        // 3. Perform the Segue
        self.prepare(for: self.commonSegue, sender: self)
        self.commonSegue.perform()
    }
    ...
}
jqgsninimo
la source
Vous appelez à self.prepare(for: self.commonSegue, sender: self)partir de votre méthode d'action. Quel est alors l'intérêt de comparer if self.commonSegue == segue {...}dans la prepare(for:sender)méthode?
nayem
@nayem: Dans prepare(for:sender:), vous pouvez configurer le contrôleur de vue de destination avant qu'il ne soit affiché. Bien sûr, vous pouvez également le faire en actionFunction.
jqgsninimo
@nayem: La raison pour laquelle je fais cela est d'essayer d'être cohérent avec le traitement des autres séquences.
jqgsninimo