web-dev-qa-db-fra.com

Lier des contrôleurs de vue enfant à un contrôleur de vue parent dans le storyboard

Pouvez-vous associer des contrôleurs de vue enfant à un contrôleur de vue de conteneur personnalisé dans Storyboard?

Je peux lier des contrôleurs de vue enfant à un contrôleur de vue d'onglet et je peux lier un contrôleur de vue à un contrôleur de navigation.

Que dois-je faire pour le conteneur VC pour accepter les VC enfants?

57

Comme une sorte de combo des réponses de Caleb et Matt, j'ai fait:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"cpdc_check_embed"]) {
        self.checkVC = segue.destinationViewController;
    }
}

... où checkVC est une propriété sur le contrôleur de conteneur:

@property (weak,nonatomic) PXPCheckViewController * checkVC;

Il vous suffit de définir votre embed segue's Storyboard ID à tout ce que vous voulez (dans ce cas, cpdc_check_embed):

check embed screen in Xcode

... puis vérifiez l'identifiant dans -prepareForSegue:sender:.

Toujours pas un débouché, mais plus propre que Matt (à mon humble avis) et plus spécifique que Caleb, et vous obtenez toujours un storyboard à la recherche de Nice:

enter image description here

75
Ben Mosher

Le storyboard traite très bien les contrôleurs de vue de conteneur intégrés, affichant les séquences aux contrôleurs de vue enfant/racine afin que les relations soient clairement affichées. Il est également agréable de voir les enfants et les contrôleurs de vue parents séparés en différentes scènes.

Si vous souhaitez obtenir cet effet dans votre propre projet, il existe une astuce qui n'est pas parfaite mais très simple. Dans mon exemple, supposons que j'ai un contrôleur de vue de conteneur qui agit comme un contrôleur de barre d'onglets avec seulement deux onglets, "gauche" et "droite". Je veux qu'une scène représente le contrôleur de vue parent et deux scènes distinctes représentent à la fois le contrôleur de vue enfant "gauche" et le contrôleur de vue enfant "droit".

Même si cela est impossible, ce serait bien si je pouvais créer des IBOutlet à partir du contrôleur de vue de conteneur vers ses enfants dans différentes scènes, puis lorsque mon contrôleur de vue de conteneur est affiché, configurez les relations parent/enfant selon les règles décrivaient le documentation UIViewController . Si nous avions des références à nos contrôleurs de vue enfant "gauche" et "droite", nous pourrions configurer les relations sans problème.

La solution standard à ce problème de référencement consiste à créer des références aux contrôleurs de vue enfant en faisant glisser les prises Object dans la scène du contrôleur de vue conteneur, puis en spécifiant leur type de classe comme étant des instances des classes de contrôleur de vue enfant.

Afin de garder les enfants séparés dans différentes scènes comme les conteneurs intégrés d'Apple, cependant, nous utiliserons une astuce différente. Supposons d'abord que les propriétés suivantes soient déclarées dans notre classe de conteneur, ContainerViewController:

@property (nonatomic, strong, readwrite) UIViewController *leftViewController;
@property (nonatomic, strong, readwrite) UIViewController *rightViewController;

Dans notre storyboard, sélectionnez la scène représentant le contrôleur de vue "gauche". Dans l'inspecteur d'attributs, définissez la propriété identifier du contrôleur de vue sur "cvc_leftViewController" ("cvc_" fait référence à ContainerViewController, mais vraiment l'identifiant peut être tout ce que vous voulez). Faites de même pour la scène du contrôleur de vue de droite, en définissant son identifiant sur "cvc_rightViewController".

Insérez maintenant le code suivant dans la méthode ContainerViewController de viewDidLoad:

if (self.storyboard) {
    _leftViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_leftViewController"];
    _rightViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"cvc_rightViewController"];
}

Lorsque ContainerViewController est chargé à partir du storyboard, il récupérera les contrôleurs de vue "gauche" et "droit" de leurs scènes respectives et leur fixera des références via ses propriétés. Maintenant que vous contrôlez les instances du contrôleur de vue enfant, vous pouvez configurer les relations parent/enfant comme vous le souhaitez. Pour savoir comment procéder correctement, reportez-vous à la documentation UIViewController .

Cette astuce n'est pas parfaite et comporte de nombreuses mises en garde, mais si vous faites attention, vous pouvez la faire fonctionner correctement pour votre projet.

Edit: Bien que cela soit complètement inutile et ne signifie rien, si vous voulez vraiment vraiment avoir les connexions d'affichage du storyboard de votre conteneur à votre vue enfant contrôleurs, tout comme les conteneurs intégrés d'Apple, utilisez simplement ma méthode ci-dessus, puis configurez des séquences directement entre la scène du conteneur et les scènes enfant, et ne réalisez tout simplement jamais ces séquences. Maintenant, tout fonctionnera correctement et sera joli aussi.

15
Matt

Pouvez-vous associer des contrôleurs de vue enfant à un contrôleur de vue de conteneur personnalisé dans Storyboard?

Je pense que ce que vous demandez ici, c'est comment connecter un contrôleur de vue dans une scène à une sortie d'un contrôleur de vue dans une scène différente. Je ne pense pas que ce soit possible, peut-être parce que la machinerie du storyboard peut ne pas avoir toutes les scènes d'un storyboard chargées en même temps.

Vous posez probablement cette question parce que vous souhaitez transmettre des informations d'un contrôleur de vue à un autre lorsque vous passez d'une scène à l'autre. La façon de procéder lorsque vous travaillez avec des story-boards consiste à remplacer -prepareForSegue:sender: dans un ou les deux contrôleurs de vue affectés par la séquence. L'objet UIStoryboardSegue fourni dans le paramètre segue possède les propriétés sourceViewController et destinationViewController, ainsi qu'une propriété identifier. Vous pouvez utiliser ces propriétés pour identifier la séquence qui est sur le point de transférer des données entre les contrôleurs de vue.

Le blog de Ray Wenderlich propose un joli didacticiel en deux parties sur l'utilisation des storyboards qui peut vous aider:

  • Partie 1 couvre la mise en place d'un projet de storyboard, l'ajout de scènes et la création de séquences.

  • Partie 2 traite de l'utilisation des séquences pour faire la transition entre les scènes, y compris la méthode prepareForSeque mentionnée ci-dessus.

iOS 5 permet à plusieurs contrôleurs de vue d'être actifs dans la même scène (bien qu'un seul devrait toujours être en charge), donc une seule scène dans votre storyboard peut avoir plusieurs contrôleurs. Vous pouvez utiliser des prises pour connecter ces contrôleurs entre eux, et vous pouvez configurer ces connexions de la même manière que dans IB: faites glisser-déplacer d'un contrôleur vers un autre dans la même scène. La liste des prises habituelles s'ouvre pour vous permettre de choisir la prise à connecter.

7
Caleb

La clé pour utiliser plusieurs contrôleurs dans une scène (ce que je crois que vous recherchez ici) consiste à utiliser le mystérieux objet de la liste des objets dans IB pour représenter l'autre contrôleur de vue et à raccorder ses prises.

Cette réponse Comment créer un conteneur de contrôleur de vue personnalisé en utilisant le storyboard dans iOS 5 devrait aider j'espère. La réponse fournit également un exemple d'application qui est très utile.

5
Matt__C

Le problème avec la réponse de @ Ben (autrement raisonnable) est qu'elle ne fonctionne qu'à un niveau d'imbrication. Au-delà de cela, il faudrait que chaque VC suivant) soit personnalisé pour enregistrer le contrôleur de vue d'imbrication dans prepareForSegue.

Pour résoudre ce problème, j'ai passé trop de temps à explorer un index basé sur NSObject que vous pourriez ajouter au Storyboard, lier à une scène, et qui enregistrerait ensuite son parent VC dans un index global, basé sur le type et la restaurationId. Cela fonctionne/peut fonctionner, mais demande trop d'efforts à la fin, et nécessite toujours le processus en deux étapes de liaison visuelle et de recherche par programme.

Pour moi, la solution la plus simple et la plus générale consiste à descendre paresseusement la hiérarchie du contrôleur de vue

Dans mon projet de test simple, j'ai ajouté les lignes suivantes à viewDidLoad:

    self.left.data = [
        "Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.",
        "De carne lumbering animata corpora quaeritis." ]

left est défini comme:

lazy var left:CollectionViewController = { [unowned self] in
    return self.childViewControllerWithId("Left") as! CollectionViewController }()

et childViewControllerWithId est défini comme:

extension UIViewController {
    func childViewControllerWithId(rid:String) -> UIViewController? {

        // check immediate child controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if vc.restorationIdentifier == rid { return vc }
        }

        // check nested controllers
        for vc in self.childViewControllers as! [UIViewController] {
            if let vc = vc.childViewControllerWithId(rid) {
                return vc
            }
        }

        assert(false, "check your assumptions")
        return nil
    }
}

Notez que vous pouvez faire d'autres variantes de find en fonction du type, si besoin est. Notez également que ce qui précède nécessite que vous définissiez l'ID de restauration dans le fichier Storyboard. Si vous n'aviez pas d'instances répétées du même contrôleur de vue, l'utilisation de type serait plus facile.

Et pour énoncer ce qui est, espérons-le, évident, vous n'avez pas besoin d'implémenter prepareForSegue, ni d'utiliser le chargement paresseux, il vous suffit d'appeler find(...).

4
Chris Conover