Disons que j'ai plusieurs contrôleurs de vue dans mon application Swift et que je souhaite pouvoir transmettre des données entre eux. Si je suis plusieurs niveaux plus bas dans une pile de contrôleurs de vue, comment puis-je transmettre des données à un autre contrôleur de vue? Ou entre les onglets dans un contrôleur d'affichage de la barre d'onglets?
(Remarque, cette question est une "sonnerie".) On en pose tellement que j'ai décidé d'écrire un tutoriel sur le sujet. Voir ma réponse ci-dessous.
Réponses:
Votre question est très large. Suggérer qu'il existe une solution fourre-tout simple à chaque scénario est un peu naïf. Alors, passons en revue certains de ces scénarios.
Le scénario le plus fréquemment posé sur Stack Overflow dans mon expérience est la simple transmission d'informations d'un contrôleur de vue à l'autre.
Si nous utilisons un storyboard, notre premier contrôleur de vue peut remplacer
prepareForSegue
, ce qui est exactement ce à quoi il sert. UnUIStoryboardSegue
objet est passé lorsque cette méthode est appelée, et il contient une référence à notre contrôleur de vue de destination. Ici, nous pouvons définir les valeurs que nous voulons transmettre.Sinon, si nous n'utilisons pas de storyboards, nous chargeons notre contrôleur de vue à partir d'une pointe. Notre code est alors légèrement plus simple.
Dans les deux cas,
myInformation
est une propriété sur chaque contrôleur de vue contenant toutes les données qui doivent être transmises d'un contrôleur de vue au suivant. Ils ne doivent évidemment pas avoir le même nom sur chaque contrôleur.Nous pourrions également souhaiter partager des informations entre les onglets dans un fichier
UITabBarController
.Dans ce cas, c'est potentiellement encore plus simple.
Tout d'abord, créons une sous-classe de
UITabBarController
et attribuons-lui des propriétés pour les informations que nous voulons partager entre les différents onglets:Maintenant, si nous construisons notre application à partir du storyboard, nous changeons simplement la classe de notre contrôleur de barre d'onglets de la valeur par défaut
UITabBarController
àMyCustomTabController
. Si nous n'utilisons pas de storyboard, nous instancions simplement une instance de cette classe personnalisée plutôt que laUITabBarController
classe par défaut et y ajoutons notre contrôleur de vue.Maintenant, tous nos contrôleurs de vue dans le contrôleur de la barre d'onglets peuvent accéder à cette propriété en tant que telle:
Et en sous-classant
UINavigationController
de la même manière, nous pouvons adopter la même approche pour partager des données sur une pile de navigation entière:Il existe plusieurs autres scénarios. En aucun cas, cette réponse ne les couvre tous.
la source
prepareForSegue
. C'est dommage que cette observation très simple soit perdue parmi les autres réponses et digressions ici.prepareForSegue
ou d'autres transferts directs d'informations dans presque tous les scénarios et ensuite simplement être d'accord avec les novices lorsqu'ils se présentent avec le scénario pour lequel ces situations ne fonctionnent pas et nous devons ensuite leur enseigner ces approches plus globales.Cette question revient tout le temps.
Une suggestion consiste à créer un singleton de conteneur de données: un objet qui est créé une et une seule fois dans la vie de votre application et qui persiste pendant toute la durée de vie de votre application.
Cette approche est bien adaptée à une situation où vous avez des données d'application globales qui doivent être disponibles / modifiables dans différentes classes de votre application.
D'autres approches, telles que la mise en place de liens unidirectionnels ou bidirectionnels entre les contrôleurs de vue, sont mieux adaptées aux situations où vous transmettez des informations / messages directement entre les contrôleurs de vue.
(Voir la réponse de nhgrif, ci-dessous, pour d'autres alternatives.)
Avec un singleton de conteneur de données, vous ajoutez une propriété à votre classe qui stocke une référence à votre singleton, puis utilisez cette propriété chaque fois que vous en avez besoin.
Vous pouvez configurer votre singleton pour qu'il enregistre son contenu sur le disque afin que l'état de votre application persiste entre les lancements.
J'ai créé un projet de démonstration sur GitHub montrant comment vous pouvez le faire. Voici le lien:
Projet SwiftDataContainerSingleton sur GitHub Voici le README de ce projet:
SwiftDataContainerSingleton
Une démonstration de l'utilisation d'un singleton de conteneur de données pour enregistrer l'état de l'application et le partager entre les objets.
La
DataContainerSingleton
classe est le singleton réel.Il utilise une constante statique
sharedDataContainer
pour enregistrer une référence au singleton.Pour accéder au singleton, utilisez la syntaxe
L'exemple de projet définit 3 propriétés dans le conteneur de données:
Pour charger la
someInt
propriété à partir du conteneur de données, vous utiliserez un code comme celui-ci:Pour enregistrer une valeur dans someInt, vous utiliserez la syntaxe:
La
init
méthode de DataContainerSingleton ajoute un observateur pour leUIApplicationDidEnterBackgroundNotification
. Ce code ressemble à ceci:Dans le code de l'observateur, il enregistre les propriétés du conteneur de données dans
NSUserDefaults
. Vous pouvez également utiliserNSCoding
, Core Data ou diverses autres méthodes pour enregistrer les données d'état.La
init
méthode de DataContainerSingleton tente également de charger les valeurs enregistrées pour ses propriétés.Cette partie de la méthode init ressemble à ceci:
Les clés de chargement et d'enregistrement des valeurs dans NSUserDefaults sont stockées sous forme de constantes de chaîne faisant partie d'une structure
DefaultsKeys
, définie comme ceci:Vous référencez l'une de ces constantes comme ceci:
Utilisation du singleton du conteneur de données:
Cet exemple d'application fait un usage trival du singleton du conteneur de données.
Il existe deux contrôleurs de vue. Le premier est une sous-classe personnalisée de UIViewController
ViewController
et le second est une sous-classe personnalisée de UIViewControllerSecondVC
.Les deux contrôleurs de vue ont un champ de texte sur eux, et tous deux chargent une valeur de la
someInt
propriété du conteneur de données singlelton dans le champ de texte de leurviewWillAppear
méthode, et tous deux enregistrent la valeur actuelle du champ de texte dans le `someInt 'du conteneur de données.Le code pour charger la valeur dans le champ de texte est dans la
viewWillAppear:
méthode:Le code pour enregistrer la valeur modifiée par l'utilisateur dans le conteneur de données se trouve dans les
textFieldShouldEndEditing
méthodes des contrôleurs de vue :Vous devez charger des valeurs dans votre interface utilisateur dans viewWillAppear plutôt que viewDidLoad afin que votre interface utilisateur se mette à jour chaque fois que le contrôleur de vue est affiché.
la source
Swift 4
Il existe de nombreuses approches pour la transmission rapide des données. Ici, j'ajoute certaines des meilleures approches de celui-ci.
1) Utilisation de StoryBoard Segue
Les séquences de storyboard sont très utiles pour transmettre des données entre les contrôleurs de vue source et de destination et vice versa également.
2) Utilisation des méthodes de délégué
ViewControllerD
ViewControllerC
la source
ViewControllerA
laViewControllerB
. Je viens de coller l'extrait de code au bas de monViewControllerA.swift
(oùViewControllerA.swift
est en fait le nom de votre fichier, bien sûr) juste avant la dernière accolade. "prepare
" est en fait une fonction préexistante intégrée spéciale dans une classe donnée [qui ne fait rien], c'est pourquoi vous devez "override
" le faireUne autre alternative consiste à utiliser le centre de notification (NSNotificationCenter) et à publier des notifications. C'est un couplage très lâche. L'expéditeur d'une notification n'a pas besoin de savoir ou de se soucier de qui écoute. Il publie juste une notification et l'oublie.
Les notifications sont bonnes pour le passage de messages un à plusieurs, car il peut y avoir un nombre arbitraire d'observateurs à l'écoute d'un message donné.
la source
Au lieu de créer un contrôleur de données singelton, je suggérerais de créer une instance de contrôleur de données et de la transmettre. Pour prendre en charge l'injection de dépendances, je créerais d'abord un
DataController
protocole:Ensuite, je créerais une
SpecificDataController
classe (ou n'importe quel nom serait actuellement approprié):La
ViewController
classe devrait alors avoir un champ pour contenir ledataController
. Notez que le type dedataController
est le protocoleDataController
. De cette façon, il est facile de changer d'implémentation de contrôleur de données:Dans
AppDelegate
nous pouvons définir le viewControllerdataController
:Lorsque nous passons à un autre viewController, nous pouvons transmettre le
dataController
:Désormais, lorsque nous souhaitons désactiver le contrôleur de données pour une tâche différente, nous pouvons le faire dans le
AppDelegate
et nous n'avons pas à modifier un autre code utilisant le contrôleur de données.C'est bien sûr exagéré si nous voulons simplement transmettre une seule valeur. Dans ce cas, il vaut mieux suivre la réponse de nhgrif.
Avec cette approche, nous pouvons séparer la vue de la partie logique.
la source
Comme @nhgrif l'a souligné dans son excellente réponse, les VC (contrôleurs de vue) et d'autres objets peuvent communiquer entre eux de nombreuses manières différentes.
Le singleton de données que j'ai décrit dans ma première réponse concerne vraiment plus le partage et la sauvegarde de l'état global que la communication directe.
La réponse de nhrif vous permet d'envoyer des informations directement de la source au VC de destination. Comme je l'ai mentionné en réponse, il est également possible de renvoyer des messages de la destination à la source.
En fait, vous pouvez configurer un canal actif unidirectionnel ou bidirectionnel entre différents contrôleurs de vue. Si les contrôleurs de vue sont liés via une séquence de storyboard, le temps de configurer les liens est dans la méthode prepareFor Segue.
J'ai un exemple de projet sur Github qui utilise un contrôleur de vue parent pour héberger 2 vues de table différentes en tant qu'enfants. Les contrôleurs de vue enfants sont liés à l'aide de segues incorporés, et le contrôleur de vue parent relie des liens bidirectionnels avec chaque contrôleur de vue dans la méthode prepareForSegue.
Vous pouvez trouver ce projet sur github (lien). Je l'ai cependant écrit en Objective-C et je ne l'ai pas converti en Swift, donc si vous n'êtes pas à l'aise avec Objective-C, cela peut être un peu difficile à suivre
la source
SWIFT 3:
Si vous avez un storyboard avec des segues identifiées, utilisez:
Bien que si vous faites tout par programme, y compris la navigation entre différents UIViewControllers, utilisez la méthode:
Remarque: pour utiliser la deuxième façon dont vous devez créer votre UINavigationController, vous poussez UIViewControllers sur, un délégué et il doit se conformer au protocole UINavigationControllerDelegate:
la source
Cela dépend du moment où vous souhaitez obtenir des données.
Si vous souhaitez obtenir des données quand vous le souhaitez, vous pouvez utiliser un modèle singleton. La classe de modèle est active pendant l'exécution de l'application. Voici un exemple du modèle singleton.
Si vous souhaitez obtenir des données après toute action, vous pouvez utiliser NotificationCenter.
la source