Comment ajouter une sous-vue qui a son propre UIViewController en Objectif-C?

je me bats avec des subviews qui ont leur propre UIViewControllers . J'ai un UIViewController avec une vue (rose clair) et deux boutons sur un toolbar . Je veux le bleu de l'affichage lorsque le premier bouton est pressé et le jaune d'affichage avec le deuxième bouton est pressé. Ce serait facile si je voulais juste afficher une vue. Mais la vue bleue contiendra une table, donc il a besoin de son propre contrôleur. C'était ma première leçon. J'ai commencé avec cette question ainsi où j'ai appris que j'avais besoin d'un contrôleur pour la table.

donc, je vais reculer et faire quelques petits pas ici. Ci-dessous une image d'un point de départ simple avec mon utilitaire ViewController (le contrôleur de vue principale) et les deux autres contrôleurs (bleu et jaune). Imaginez que lorsque l'utilitaire ViewController (la vue principale) est affiché pour la première fois la vue bleue (par défaut) sera affichée où la vue rose est située. Les utilisateurs pourront cliquer sur les deux les boutons pour aller et venir et la vue rose ne seront jamais affichés. Je veux juste que la vue bleue aille là où la vue rose est et la vue jaune, là où la vue rose est. J'espère que cela a du sens.

Simple Storyboard image

j'essaie d'utiliser addChildViewController . De ce que j'ai vu, il y a deux façons de le faire: la vue de conteneur dans le storyboard ou addChildViewController programmatiquement. Je veux le faire programmatiquement. Je n'ai pas souhaitez utiliser un NavigationController ou une barre d'Onglet. Je veux juste ajouter les controllers et enfoncer la vue correcte dans la vue rose quand le bouton associé est appuyé.

ci-dessous est le code que j'ai jusqu'à présent. Tout ce que je veux c'est afficher la vue bleue où est la vue rose. De ce que j'ai vu je devrais être en mesure de juste addChildViewController et addSubView. Ce code ne fait pas ça pour moi. Ma confusion est de mettre le meilleur de moi. Quelqu'un peut m'aider à avoir la vue bleue? où est la vue rose?

ce code n'est pas destiné à faire autre chose que d'afficher la vue bleue dans viewDidLoad.

IDUtilityViewController.h

#import <UIKit/UIKit.h>

@interface IDUtilityViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *utilityView;
@end

IDUtilityViewController.m

#import "IDUtilityViewController.h"
#import "IDAboutViewController.h"

@interface IDUtilityViewController ()
@property (nonatomic, strong) IDAboutViewController *aboutVC;
@end

@implementation IDUtilityViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.aboutVC = [[IDAboutViewController alloc]initWithNibName:@"AboutVC" bundle:nil];
    [self addChildViewController:self.aboutVC];
    [self.aboutVC didMoveToParentViewController:self];
    [self.utilityView addSubview:self.aboutVC.aboutView];
}

@end

--------------------------MODIFIER------------------------------

Auto.aboutVC.aboutView est nul. Mais je l'ai branché dans le storyboard . Dois-je encore l'instancier?

enter image description here

44
demandé sur Community 2014-05-01 02:21:57

2 réponses

ce post qui date des premiers jours de l'iOS moderne, a généralement la dernière syntaxe, comme Swift 4 actuellement. Si vous commencez avec iOS, autolayout, etc il vous fera aller.

Dans iOS aujourd'hui "tout est un conteneur view" . Il est la façon de base que vous faites des applications aujourd'hui.

une application peut être si simple qu'elle n'a qu'un seul point de vue. Mais même dans ce cas, chaque "chose" sur l'écran est un conteneur de vue.

c'est aussi simple...


(a) faites glisser une vue de conteneur dans votre scène...

faites glisser une vue de conteneur dans votre vue de scène. (Tout comme vous traîneriez dans, disons, un UIButton.)

la vue du conteneur est la chose brune dans cette image. Il est en fait à l'intérieur votre scène vue.

enter image description here

quand vous faites glisser une vue de conteneur dans votre vue de scène, Xcode vous donne automatiquement deux choses :

  1. , Vous obtenez le conteneur view à l'intérieur de votre scène vue , et,

  2. , vous obtenez un tout nouveau UIViewController , qui est juste assis quelque part sur le blanc de votre story-board .

les deux sont connecté avec le "symbole maçonnique" chose - expliquée ci-dessous!


(B) cliquez sur ce nouveau contrôleur de vue (la nouvelle chose Xcode fait pour vous quelque part sur la zone blanche, pas la chose à l'intérieur de votre scène )... et, modifier la classe!

c'est aussi simple que ça.

C'est fini.


Voici la même chose expliquée visuellement.

Avis conteneur affichage à (A) .

Avis contrôleur à (B) .

shows a container view and the associated view controller

cliquez sur B. (C'est B - Pas A!)

Aller à l'inspecteur en haut à droite. Notez qu'il est écrit "UIViewController "

[ enter image description here ][3]

changez-le pour votre propre classe personnalisée, qui est UIViewController.

enter image description here

donc, j'ai une classe Swift Snap qui est une UIViewController .

enter image description here

donc où il est dit " UIViewController "dans L'Inspecteur que j'ai tapé dans"Snap".

(comme d'habitude, Xcode remplira automatiquement" Snap "lorsque vous commencerez à taper "Snap"...".)

Que tout y est, vous avez terminé.


Comment changer le conteneur view - dire, à la vue de la table.

ainsi, lorsque vous cliquez pour ajouter une vue conteneur, Apple vous donne automatiquement un contrôleur de vue lié, Assis sur le storyboard.

, Comme il arrive (2017): il en fait un UIViewController par défaut.

c'est stupide: il devrait demander quel type vous avez besoin. Par exemple, vous avez souvent besoin d'une vue de table. Voici comment le changer pour quelque chose de différent:

au moment de l'écriture, Xcode vous donne un UIViewController par défaut. Disons que vous voulez un UICollectionViewController à la place:

(i) faites glisser une vue de conteneur dans votre scène. Regardez le Controller UIViewController sur le storyboard que Xcode vous donne par défaut.

(ii) faites glisser un nouveau UICollectionViewController n'importe où sur la zone blanche principale du storyboard.

(iii) cliquez sur le conteneur vue à l'intérieur de votre scène. Cliquez sur l'Inspecteur des connexions. Notez qu'il y a une "séquence déclenchée". souris sur la " séquence déclenchée "et noter que Xcode met en évidence tout le Controller UIViewController indésirable.

(iv) cliquez sur le" x "pour réellement supprimer qui a déclenché la séquence.

(v) faites Glisser à partir de qui a Déclenché Segue (viewDidLoad est le seul choix.) Glissez à travers le storyboard vers votre nouveau contrôleur Uicollection ViewController. Lâchez - la et un pop-up apparaît. Vous devez sélectionner embed .

(vi) simplement supprimer tous les UIViewController indésirables. Vous avez terminé.

version courte: supprimer le UIViewController indésirable. Mettez un nouveau UICollectionViewController sur le storyboard. Control-drag from: Les connexions de la vue conteneur-Trigger Segue - viewDidLoad, to, votre nouveau contrôleur. Assurez-vous de sélectionner "embed" sur le popup.


entrer l'Identificateur de texte...

vous aurez un de ces " carré dans un carré " objets de symbole maçonnique: il est sur la" ligne bendy " reliant votre vue de conteneur avec le contrôleur de vue.

la chose" symbole maçonnique " est la suite.

enter image description here

sélectionnez la séquence en cliquant sur sur le" symbole maçonnique " chose.

Regardez à votre droite.

Vous DOIT taper un identificateur de texte pour la séquence.

vous décidez du nom. cela peut être n'importe quelle chaîne de texte. un choix raisonnable est souvent"segueClassName".

Si vous suivez ce modèle, TOUS vos segments seront appelés segueClockView, seguePersonSelector, segueSnap, segueCards et ainsi de suite.

ensuite, où utilisez-vous cet identificateur de texte?


Comment connecter " à " l'enfant du contrôleur...

ensuite, faites ce qui suit, en code, dans le ViewController de toute la scène.

disons que vous avez trois vues de conteneur dans la scène. Chacun la vue de conteneur contient un contrôleur différent, dire "Snap"," Horloge "et"autre".

dernière syntaxe Swift3 (2017)

var snap:Snap?
var clock:Clock?
var other:Other?

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.identifier == "segueSnap")
            { snap = (segue.destination as! Snap) }
    if (segue.identifier == "segueClock")
            { clock = (segue.destination as! Clock) }
    if (segue.identifier == "segueOther")
            { other = (segue.destination as! Other) }
}

c'est aussi simple que ça. Vous connectez une variable pour vous référer aux contrôleurs, en utilisant l'appel prepareForSegue .


comment se connecter dans "l'autre direction", jusqu'au parent...

dire que vous êtes "dans" le contrôleur que vous avez mis dans un conteneur view ("Snap" dans l'exemple).

il peut être déroutant d'arriver au contrôleur de vue" boss"au-dessus de vous ("Dash" dans l'exemple). Heureusement, c'est aussi simple que cela:

// Dash is the overall scene.
// Here we are in Snap. Snap is one of the container views inside Dash.

class Snap {

var myBoss:Dash?    
override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear
    super.viewDidAppear(animated)
    myBoss = self.parent as? Dash
}

Critique: ne fonctionne qu'à partir de viewDidAppear ou plus tard. Ne fonctionnera pas dans viewDidLoad .

C'est fini.


Important: que seulement travaux pour conteneur de vues.

conseil avancé Important: N'oubliez pas, qui ne fonctionne que pour les vues de conteneur.

de nos jours avec les identificateurs storyboard, il est banal de simplement pop Nouvelles vues sur l'écran (plutôt que dans le développement Android). Donc, disons que l'utilisateur veut modifier quelque chose...

    // let's just pop a view on the screen.
    // this has nothing to do with container views
    //
    let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit
    e.modalPresentationStyle = .overCurrentContext
    self.present(e, animated: false, completion: nil)

lors de l'utilisation d'une vue de conteneur, il est garanti que Dash sera le contrôleur de vue parent de Enfichable.

cependant c'est pas nécessairement le cas lorsque vous utilisez instantiateViewController.

très déconcertant, dans iOS Le contrôleur de vue parent est non relié à la classe qui l'a instancié. ( pourrait être le même, mais, habituellement, il n'est pas le même.) Le dessin self.parent est seulement pour les vues des conteneurs.

(pour un résultat similaire dans le modèle instanciateviewcontroller, vous devez utiliser un protocole et un délégué, en se rappelant que le délégué sera un maillon faible.)


prepareForSegue mal nommé...

il est intéressant de noter que "prepareForSegue" est un vraiment mauvais nom!

"prepareForSegue" est utilisé à deux fins: le chargement d'un conteneur de points de vue, et, segueing entre les scènes.

mais dans la pratique, vous très rarement segue between scenes! alors que presque chaque application a beaucoup, beaucoup, conteneurs vues comme une question de cours.

cela aurait beaucoup plus de sens si" prepareForSegue "était appelé quelque chose comme"loadingContainerView".


plus d'un...

une situation courante est: vous avez une petite zone sur le l'écran, où vous voulez afficher l'un des un certain nombre de vue différents contrôleurs. Par exemple, l'un des quatre widgets.

la manière la plus simple de le faire: il suffit d'avoir quatre vues différentes des conteneurs tous assis dans le même zone identique . Dans votre code, il vous suffit de cacher les quatre et de mettre celui que vous voulez visible.

sur le storyboard, avoir un vide "holder" UIView, qui tient simplement les quatre conteneurs vue. Vous pouvez alors dimensionner ou déplacer les quatre à la fois en dimensionnant ou en déplaçant le "support". Dans votre code, il vous suffit d'avoir quatre prises UIView , une pour chacune des vues des conteneurs. Copiez et collez le code ci-dessus, "comment se connecter au contrôleur enfant", pour connecter les quatre contrôleurs de vue contenus.


Note - les références du Storyboard arrivent!

comme SimplGy le souligne ci-dessous " iOS 9's Storyboard Les références rendent les vues de conteneur encore plus impressionnant. Vous pouvez définir votre vue réutilisable (controller) où que vous le souhaitiez et la référencer à partir de n'importe quelle vue de conteneur dans plusieurs storyboards modulaires."

Notez aussi que - assez déconcertant-souvent aujourd'hui vous juste ne vous embêtez pas avec des vues de conteneur!

vous simplement instantiateViewController#withIdentifier dans de nombreuses situations.

mais notez le "gotchya" à propos de .parent expliqué ci-dessus. Le point entier des vues de conteneur est que vous êtes instantanément et simplement assuré de la chaîne de parent.

si vous allez avec instantiateViewController#withIdentifier en utilisant une référence de scénario-maquette, vous devez déconner avec un protocole et un délégué (en se rappelant que le délégué sera un maillon faible). Mais ensuite, vous pouvez utiliser "à la volée" souplesse n'importe où.

par contraste, en utilisant un "fixe", pour ainsi dire, vue conteneur est extrêmement simple, et vous raccorder instantanément entre le parent et l'enfant, comme expliqué ci-dessus.

118
répondu Fattie 2018-08-14 06:28:49

je vois deux problèmes. Tout d'abord, puisque vous faites les contrôleurs dans le storyboard , vous devriez les instancier avec instantiateViewControllerWithIdentifier: , pas initWithNibName:bundle: . Deuxièmement, lorsque vous ajoutez de la vue comme une sous-vue, vous devriez lui donner un cadre. So,

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:@"aboutVC"]; // make sure you give the controller this same identifier in the storyboard
    [self addChildViewController:self.aboutVC];
    [self.aboutVC didMoveToParentViewController:self];
    self.aboutVC.view.frame = self.utilityView.bounds;
    [self.utilityView addSubview:self.aboutVC.aboutView];
}
4
répondu rdelmar 2015-11-16 12:06:51