Je me bats avec des sous-vues qui ont leur propre UIViewControllers
. J'ai un UIViewController
avec une vue (rose clair) et deux boutons sur un toolbar
. Je veux que la vue bleue s'affiche lorsque le premier bouton est enfoncé et que la vue jaune s'affiche avec le deuxième bouton. Cela devrait être facile si je voulais simplement afficher une vue. Mais la vue bleue contiendra une table, elle a donc besoin de son propre contrôleur. C'était ma première leçon. J'ai commencé avec cette SO question où j'ai appris que j'avais besoin d'un contrôleur pour la table.
Donc, je vais reculer et faire quelques pas ici. Ci-dessous est une image d'un point de départ simple avec mon utilitaire ViewController
(le contrôleur de vue principal) 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 là où se trouve la vue rose. Les utilisateurs pourront cliquer sur les deux boutons pour aller et venir et la vue rose ne sera JAMAIS affichée. Je veux juste que la vue bleue aille là où la vue rose est et que la vue jaune va où la vue rose est. J'espère que cela a du sens.
J'essaie d'utiliser addChildViewController
. D'après ce que j'ai vu, il y a deux façons de procéder: La vue conteneur dans storyboard
ou addChildViewController
par programme. Je veux le faire par programme. Je ne veux pas utiliser un NavigationController
ou une barre d'onglets. Je veux juste ajouter les contrôleurs et pousser la vue correcte dans la vue rose lorsque le bouton associé est enfoncé.
Voici le code que j'ai jusqu'à présent. Tout ce que je veux faire, c'est afficher la vue bleue là où se trouve la vue rose. D'après ce que j'ai vu, je devrais pouvoir simplement addChildViewController
et ajouterSubView. Ce code ne fait pas ça pour moi. Ma confusion prend le dessus sur moi. Quelqu'un peut-il m'aider à afficher la vue bleue là où se trouve 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
-------------------------- EDIT -------------- ----------------
Self.aboutVC.aboutView est nul. Mais je l'ai câblé dans le storyboard
. Dois-je encore l'instancier?
Ce message qui date des premiers jours de l'iOS moderne, a généralement la dernière syntaxe, telle que Swift 4 actuellement. Si vous commencez avec iOS, la mise en page automatique, etc., cela vous permettra de continuer.
Dans iOS aujourd'hui "tout est une vue de conteneur" . C'est la façon de base de créer des applications aujourd'hui.
Une application peut être si simple qu'elle n'a qu'une seule vue. Mais même dans ce cas, chaque "chose" à l'écran est une vue de conteneur.
C'est aussi simple que ça ...
Faites glisser une vue de conteneur dans votre vue de scène. (Tout comme vous traîneriez, disons, un UIButton.)
La vue du conteneur est la chose brune sur cette image. Il s'agit en fait de votre vue de scène .
Lorsque vous faites glisser une vue de conteneur dans votre vue de scène, Xcode vous donne automatiquement deux choses :
Vous obtenez la vue conteneur à l'intérieur de votre vue de scène , et,
vous obtenez un tout nouveau UIViewController
qui est juste assis quelque part sur le blanc de votre storyboard .
Les deux sont connectés avec la chose "Symbole maçonnique" - expliqué ci-dessous!
C'est vraiment aussi simple que cela.
Vous avez terminé.
Voici la même chose expliquée visuellement.
Remarquez la vue du conteneur à (A)
.
Remarquez le contrôleur à (B)
.
Cliquez sur B. (C'est B - pas A!)
Allez à l'inspecteur en haut à droite. Notez qu'il dit "UIViewController"
[] [3]
Modifiez-le en votre propre classe personnalisée, qui est un UIViewController.
Donc, j'ai une classe Swift Snap
qui est un UIViewController
.
Alors là où il est écrit "UIViewController" dans l'inspecteur, j'ai tapé "Snap".
(Comme d'habitude, Xcode complétera automatiquement "Snap" lorsque vous commencerez à taper "Snap ...".)
C'est tout ce qu'il y a à faire - vous avez terminé.
Ainsi, lorsque vous cliquez pour ajouter une vue de conteneur, Apple vous donne automatiquement un contrôleur de vue lié, assis sur le storyboard.
En fait (2017): cela en fait un UIViewController
par défaut.
C'est idiot: il devrait demander de quel type vous avez besoin. Par exemple, vous avez souvent besoin d'une vue tabulaire. Voici comment le changer en quelque chose de différent:
Au moment de l'écriture, Xcode vous donne un
UIViewController
par défaut. Supposons que vous souhaitiez unUICollectionViewController
à la place:(i) Faites glisser une vue de conteneur vers votre scène. Regardez le 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 la vue conteneur à l'intérieur de votre scène. Cliquez sur l'inspecteur de connexions. Remarquez qu'il y a une "séquence déclenchée". Passez la souris sur le "Triggered Segue" et notez que Xcode met en surbrillance tous l'UIViewController indésirable.
(iv) Cliquez sur le "x" pour réellement supprimer ce déclenchement déclenché.
(v) Faites glisser depuis ce déclencheur déclenché (viewDidLoad est le seul choix). Faites glisser le storyboard vers votre nouveau UICollectionViewController. Lâchez prise et une fenêtre contextuelle apparaît. Vous devez sélectionner embed .
(vi) Simplement supprimer tous les UIViewController indésirables. Vous avez terminé.
Version courte: supprimez le UIViewController indésirable. Mettez un nouveau UICollectionViewController
sur le storyboard. Faites glisser la souris depuis: les connexions de la vue du conteneur - Trigger Segue - viewDidLoad vers votre nouveau contrôleur. Assurez-vous de sélectionner "incorporer" dans la fenêtre contextuelle.
Vous aurez l'un de ces éléments "carré dans un carré" symbole maçonnique: il est sur la "ligne flexible" reliant votre vue de conteneur avec le contrôleur de vue .
La chose "symbole maçonnique" est la séquence.
Sélectionnez la séquence en cliquant sur la chose "symbole maçonnique".
Regardez à votre droite.
Vous [~ # ~] devez [~ # ~] saisir un identificateur de texte pour la séquence.
Vous décidez du nom. Il peut s'agir de n'importe quelle chaîne de texte. Un choix judicieux est souvent "segueClassName".
Si vous suivez ce modèle, toutes vos séquences seront appelées segueClockView, seguePersonSelector, segueSnap, segueCards, etc.
Ensuite, où utilisez-vous cet identifiant de texte?
Ensuite, procédez comme suit, dans le code, dans le ViewController de la scène entière.
Supposons que vous ayez trois vues de conteneur dans la scène. Chaque vue de conteneur contient un contrôleur différent, par exemple "Snap", "Clock" et "Other".
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 si simple. Vous connectez une variable pour faire référence aux contrôleurs, en utilisant l'appel prepareForSegue
.
Disons que vous êtes "dans" le contrôleur que vous avez mis dans une vue conteneur ("Snap" dans l'exemple).
Il peut être déroutant d'accéder 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: Fonctionne uniquement à partir de viewDidAppear
ou version ultérieure. Ne fonctionnera pas dans viewDidLoad
.
Vous avez terminé.
Astuce avancée importante: N'oubliez pas, cela ne fonctionne que pour les vues de conteneurs.
De nos jours avec les identificateurs de storyboard, il est courant de simplement afficher de nouvelles vues à l'écran (plutôt que dans 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)
Lorsque vous utilisez une vue de conteneur, IT IS GARANTI que Dash sera le contrôleur de vue parent de Snap.
Cependant c'est PAS NÉCESSAIREMENT LE CAS lorsque vous utilisez instantiateViewController.
Très confusément, dans iOS, le contrôleur de vue parent n'est pas lié à la classe qui l'a instancié. (Il pourrait être le même, mais ce n'est généralement pas le même.) Le self.parent
le modèle est uniquement pour les vues de conteneur.
(Pour un résultat similaire dans le modèle instantiateViewController, vous devez utiliser un protocole et un délégué, en vous rappelant que le délégué sera un maillon faible.)
Il est à noter que "prepareForSegue" est un très mauvais nom!
"prepareForSegue" est utilisé à deux fins: le chargement des vues de conteneur et la transition entre les scènes.
Mais en pratique, vous faites très rarement la transition entre les scènes! Alors que presque toutes les applications ont de nombreuses vues de conteneurs, bien sûr.
Cela aurait beaucoup plus de sens si "prepareForSegue" était appelé quelque chose comme "loadingContainerView".
Une situation courante est la suivante: vous avez une petite zone sur l'écran, où vous souhaitez afficher l'un des différents contrôleurs de vue. Par exemple, l'un des quatre widgets.
La façon la plus simple de le faire: il suffit d'avoir quatre vues de conteneurs différentes toutes assis dans la même zone identique . Dans votre code, masquez simplement les quatre et activez celui que vous souhaitez voir.
Sur le storyboard, un UIView "titulaire" vide, qui contient simplement les quatre vues de conteneur. Vous pouvez ensuite dimensionner ou déplacer les quatre à la fois en dimensionnant ou en déplaçant le "support". Dans votre code, il vous suffit de disposer de quatre prises UIView
, une pour chacune des vues de conteneur. Copiez et collez le code ci-dessus, "Comment se connecter au contrôleur enfant", pour connecter les quatre contrôleurs de vue contenus.
Comme le souligne SimplGy ci-dessous "iOS 9 Références du storyboard rendre les vues de conteneurs encore plus impressionnantes. Vous pouvez définir votre vue réutilisable (contrôleur) où vous le souhaitez et la référencer à partir de n'importe quelle vue de conteneur dans plusieurs story-boards modulaires. "
Notez également que - plutôt confus - souvent aujourd'hui, vous ne vous embêtez pas avec les vues de conteneur!
Vous simplement instantiateViewController#withIdentifier
dans de nombreuses situations.
Mais notez le "gotchya" à propos de .parent
expliqué ci-dessus. Le point de vue des conteneurs est que vous êtes instantanément et simplement assuré de la chaîne parent.
Si vous allez avec instantiateViewController#withIdentifier
en utilisant une référence de storyboard, vous devez jouer avec un protocole et un délégué (en vous rappelant que le délégué sera un maillon faible). Mais vous pouvez ensuite l'utiliser "à la volée" partout avec souplesse.
En revanche, en utilisant un "fixe", pour ainsi dire, la vue du conteneur est extrêmement simple, et vous vous connectez instantanément entre le parent et l'enfant comme expliqué ci-dessus.
Je vois deux problèmes. Tout d'abord, puisque vous créez les contrôleurs dans le storyboard, vous devez les instancier avec instantiateViewControllerWithIdentifier:
, ne pas initWithNibName:bundle:
. Deuxièmement, lorsque vous ajoutez la vue en tant que sous-vue, vous devez lui donner un cadre. Alors,
- (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];
}