[Résolu] Transmission de variables au sein des pages d'un UIPagViewController
Bonjour,
pour éviter de surcharger en phrases inutiles (déjà , celle-là , elle était de trop, mais bon...), disons simplement, que je ne parviens pas (j'ai vraiment essayer 50 versions possibles) à transmettre des variables entre les pages de mon UIPageViewController. Les delegates ne fonctionnent pas vraiment.
Ou plus exactement, entre une première view (SB) et la première page du UIPageViewController.
Delegate, simple, genre :
delegateEtape1L1?.remplirChampEtape1L1?()
Avec bien sûr les protocoles et les fonctions aux bons endroit qui vont bien.
Car j'en ai qui fonctionnent (ceux concernant des fonds, des barre de nav - mais ils sont génériques et utiles à toutes les pages).
Mais ceux concernant les transmissions de variables vers des outlets (labels) de sous-pages spécifiques, ça merde grave.
Je fais des println("coucou") et rien n'arrive, ou si ça arrive, les variables (outlets) ne reçoivent rien, ou bien ça tombe toujours dans l'erreur "unwrapping.....).
Petite explication :
Laissons tomber les remarques sur la view storyboard ou son controller, puisque la view storyboard est associée à son controller.
Je démarre donc mon appli sur un fichier etape0 (rootViewController) dans un container avec nav à gauche comme ça - ça marche nickel.
Ensuite je transmet une variable de étape0 à etape1, qui lui est comme le viewController comme ici, et tout fonctionne parfaitement.
Dans etape1, je fais en sorte, comme sur le précédent lien, d'arriver sur la première page du UIPageViewController :
if !controllers.isEmpty {
pageController.setViewControllers([controllers[0]], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
}
ce controller[0] est justement la première page. Donc le etape1 sous-page1, et là je ne parviens pas à recevoir les variables (outlets).
J'ai essayer de déplacer les protocoles, les delegates, dans le fameux fichier contentItem..., dans le container général, dans etape1, etc. Rien n'y fait.
Je dois louper un truc.
En fait, je ne suis pas si bref que cela, mais c'est un peu complexe comme truc. Désolé.
J'évite de surcharger en code, mais si besoin, pas de pb.
Encore merci pour le temps passé à me lire.
Je deviens fou avec tous ces delegates.
On est bien d'accord, pour transmettre des variables (et plus tard je serais avec coreData) entre des views (SB) sur des champs (labels en outlets), y'a pas le choix ?
Réponses
Non, on est pas d'accord.
Pour transmettre une valeur, la manière la plus classique est de créer une propriété dans la classe qui reçoit.
La délégation est un mécanisme dans lequel une classe délègue une partie de son travail à son délégué. Pour cela, elle possède un pointeur sur son délégué, dont elle ne connaà®t pas le type exact, sinon qu'il se conforme à un protocole qu'elle-même a défini, pour savoir quelle méthode elle peut appeler.
La délégation sert essentiellement à deux choses:
- séparer les domaines. Par exemple, une UITableView utilise deux délégués (delegate et datasource), ce qui évite de la sous-classer pour accéder au modèle.
- intégrer un système de callback. Par exemple, NSURLConnection appelle son délégué pour lui signaler qu'elle a chargé des données ou qu'une erreur a été rencontrée. (on utilise aujourd'hui souvent les blocs cou closures à la place, qui s'avèrent plus pratiques).
Salut AliGator,
génial, ta réponse. C'est exactement ça.
Je transmet mes valeurs ainsi. J'y parviens parfaitement à l'aide de la propriété dans la classe qui reçoit.
Sauf que là , je suis dans le cas des pages qui constituent le tableau de pages du UIPageViewController.
Et je ne parviens pas à transmettre une valeur de l'extérieur vers la première de ces pages.
J'ai essayé la méthode de la propriété, et aussi avec un delegate qui lancerait une fonction présente dans la première page du UIPageViewController, qui elle, remplirait mon label, ou plus exactement donnerait la valeur à la propriété.
Je m'excuse pour la confusion du message précédent, ainsi que celle de celui-ci.
Mais comme souvent, ma question est super simple, mais difficile à formuler.
Encore merci pour tes précisions...
Il est la même si on le voit pas ce @Aligator
C'est compliqué ton truc. Déjà les views n'ont pas à communiquer entre-elles. Ce sont les contrôleurs qui doivent échanger des informations. Donc, pas d'outlets!
Je ne connais pas les UIPageViewControleur, mais en parcourant tes tutos je comprend qu'il y a un contrôleur maà®tre, et une liste de contrôleur secondaire. Donc pour communiquer des infos, il faut ..
Ah, je vois que tu que as trouvé ta réponse, grâce au reptile, même si je ne vois pas son explication. MP peut-être ? Ou alors t'as confondu un mammifère herbivore avec un prédateur reptilien aquatique carnivore ?
Toujours est-il que UIPageViewController contient une liste de UIViewControllers.
Ceci répond à ta première question: comment donner une valeur à label ?
Un UILabel doit être géré par un view controller. Donc le UIViewController va posséder une propriété, et c'est lui qui fixera le texte du label. Comme d'habitude.
Pour la deuxième question, ton souci est que UIPageViewController possède une liste de UIViewControllers. Deux cas se présentent:
1) les pages affichent le même type de contenu
Les contrôleurs sont des instances différentes de la même sous-classe de UIViewController.
2) les pages affichent des contenus différents
Les contrôleurs sont des instances de sous-classes différentes de UIViewController.
Déjà , dans quel cas sommes nous ?
Il y a aussi 2 cas de figures :
- les UIViewControllers sont créés au début du programme et stockés dans un tableau
- les UIViewControllers sont créés dynamiquement lors de la navigation.
Bonjour, merci pour toutes ces précisions.
J'ai bien tout compris (enfin, presque).
Ceci-dit, je vais essayer de m'expliquer et de répondre aux questions, en utilisant au maximum la bonne terminologie.
Je sais parfaitement transmettre une chaine String entre deux controllers.
Puis ensuite, celui qui la reçoit la met dans le label de la view associée (donc le outlet).
Par exemple.
puis :
et puis :
Et le label se rempli bien. ça marche aussi avec un println évidemment.
Donc aucun problème, lorsqu'il s'agit de controllers simples.
Mon soucis, est dans le cas du UIPageViewController.
Je suis dans le cas où mes pages affichent des contenus différents.
Les controllers sont tous une instance différente de la classe suivante (dixit le toto), qui pour l'instant est presque vide.
Et mes controllers sont donc de ce type :
Mes controllers sont créés au début du programme et sont stockés dans un tableau comme dans le tuto pré-cité.
Je rappelle que tout fonctionne bien. Je ne met pas ici le code du contrôler maitre, sauf si besoin. Mais il est nickel, grâce aux barmen qui m'ont bien aidé précédemment.
Petit résumé de la navigation.
Je passe de la view associée au controller Etape0ViewController à celle associée au controller Etape1ViewController sans problème, et je transmet sans problème des variables entre ces deux controllers.
Le controller Etape1ViewController est le controller maitre des sous-pages qui, elles, sont chacunes associées aux controllers Etape1L1ViewController, Etape1L2ViewController, Etape1L3ViewController, etc
Donc pour revenir sur mon problème principal :
Aucun des controllers associés aux sous-pages ne peut recevoir de variable.
J'ai tout essayé :
À partir du premier controller Etape0ViewController
Je lui transmet une chaine à partir du premier controller Etape0ViewController, à partir d'une action.
et j'ai ça comme résultat.
Tu sais que tu peux faire le même genre d'affichage avec un UINavigationController ?
On est dans une architecture Modèle-Vue-Contrôleur.
Chaque UIViewController va chercher son contenu dans le modèle. Donc, si je veux transmettre une information d'un contrôleur à l'autre, mon contrôleur va modifier le modèle.
On n'entre dans des modèles plus complexes que lorsqu'un contrôleur doit prévenir un autre que le modèle a changé " on utilisera par exemple la délégation dans ce cas.
C'est sans doute parce que quand tu demandes à afficher la donnée, celle-ci n'a pas encore été passée par l'autre contrôleur. Fais du pas-à -pas au débogueur, c'est facile à voir.
Draken, avec le UINavigationController, ça doit être hard de gérer un UIPageControl, non ?
Et en plus, mon appli, c'est des étapes et des sous-étapes, avec des navigations différentes selon le niveau.
Désolé Céroce pour la confusion de nom.
Je ne comprend pas trop ce que tu dis. Je ne suis que peu familier avec les concepts. J'en ai pourtant lu des tartines (bouquins, web) là -dessus...
Je suis convaincu que, effectivement, quand je passe une donnée à une sous-page (etape1L1), comme c'est d'abord la page mère (étape1 - le contrôler maà®tre) qui est pushé, la donnée arrive avant la sous-page. Mais je ne vois pas comment résoudre ce problème, car les sous-pages n'arrivent que par le biais du controller maà®tre qui est pushé. Pfff, compliqué à expliquer tout ça.
Bon, je viens de trouvé une façon de transmettre, mais c'est bidon, car cela va m'obliger à placer toutes les variables de tous les controllers associés à toutes les sous-pages sous-pages, qui elles contiennent des labels (par ex) qui doivent recevoir les chaines transmises, donc placer toutes ces variables dans la sous-classe de UIViewController dont ils sont une instance (ContentEtapesViewController - la classe montrée plus haut).
Et en plus je dois transmettre ces variables à partir de la fonction suivante du controller maà®tre, en ciblant ces controllers de sous-pages par leur position dans le tableau et non pas par leur nom.
J'ai essayé dans le viewDidLoad, ou le viewWillAppear, ou le viewDidAppear,
rien n'y fait. C'est la seule méthode ou ça passe. C'est déjà pas mal, mais à mon avis, je dois trouver une autre méthode, car je ne peuxpas balancer 300 variables dans le ContentEtapesViewController qui à priori ne doit contenir que les communes, et non pas les dédiées.
Qu'en pensez-vous ?
Bon, plus cool, ça marche aussi en faisant une petite fonction (enfin, qui sera énorme - 300 variable), appelée dans le viewDidLoad - Ce sera plus propre, déjà .
Pas particulièrement, mais franchement est-ce que le UIPageControl est nécessaire à ton application ? C'est minuscule et presque personne ne l'utilise pour naviguer. Cela fournit une information visuelle sur la position de la page courante, par rapport au nombre de pages totales, mais cela devient vite illisible. Combien compte-tu avoir de pages dans ton application ? Tu peux très bien le remplacer par un label "page 12/24" (par exemple).
Un UINavigationController permet de naviguer dans tout les sens.
C'est fondamental leUIPageControl.On me donne une créa, une ergonomie, une hiérarchie de vues, et je fais au mieux pour saisfaire le directeur de projet et artistique, ainsi eu le client. Je fonctionne toujours comme ça depuis que je code avec des graphistes (dans le web) et c'est ainsi que je trouvera place, et en plus j'adore ça.
Je mettrais une vidéo, lorsque cela sera un peu plus abouti, ainsi que le projet en zip.
Cela pourra servir pour les autres blaireaux comme moi
En tout cas, que penses-tu de la solution qui consiste à viser les éléments de tableau et non pas le controllers par leur nom dans le storyboard ou leur nom d'instance ?
T'as vu, je progresse dans la terminologie, non ?
Sincèrement, je ne peux pas te répondre avec le même degré de certitude qu'Ali ou Céroce. . Je n'ai jamais eu à traiter le même problème. Tu sais, je suis dans la même situation que toi, j'apprend Swift sur le tas, en réglant les problèmes au fur et à mesure. Quand je répond à quelqu'un c'est que j'ai déjà affronté et surmonté quelque chose de similaire. Et a vrai dire, je développe ma propre application en ce moment, en espérant taper du coder pour la dernière fois de ma vie.
Oui, il me parait préférable d'accéder aux contrôleurs via le tableau du contrôleur maà®tre. C'est ce que j'aurais essayé aussi.
Ha, eh bien merci pour tes judicieux conseils et remarques qui m'encouragent énormément, car parfois, je me dit que je suis vraiment à côté de la plaque. Je trouve en général tout, tout seul, et là , je ne suis pas fier d'appeler au secours après m'être éreinter...
Tu apprend swift, mais tu as, semble-t-il une bonne connaissance des concepts, en ObjectiveC, beaucoup plus que moi. 9a aide grave.
Par contre, si tu as besoin de codes spécifiques, je suis quand même bien armé, mais je n'ai surement pas ta présence d'esprit et ton esprit d'analyse. Enfin, on ne sait jamais.
J'attend un peu quelques remarques un peu plus cassantes (car je les aime bien aussi), pour marqué Résolu.
Encore merci à tous, n'hésitez pas à commenter, y'a encore de la place...
Une petite vidéo pour montrer tout ça (cela n'est qu'en construction...)
Si y'a des remarques sur le positionnement des variables dans le ContentEtapesViewController et les transmissions dans le viewWillAppear du Etape1ViewController (le controller maà®tre) avec une fonction
en tapant sur les éléments du tableau des controllers plutôt que sur les controllers par leur storyboard ou leur nom.
Parce que ce qui m'ennuie, c'est que je vais devoir transmettre les 300 variables (c'est un exemple) à chaque fois, alors que je n'ai besoin normalement que de transmettre celles du controller (associée à la sous-page), et non pas tous ceux de toutes les sous-pages, et ça c'est bien dommage, et surtout pas très conforme, lent certainement beaucoup plus lent vu le nombre de requêtes (à savoir qu'ensuite je remonte le projet avec du coreData).
Ou bien faudra faire des putains de test sur qui envoi quoi... Bonjour les dégâts.
Voilà merci pour votre contribution.
Je vois que tu n'as toujours pas compris le principe du MVC. Pas besoin de transmettre 300 variables à la fois. Je te tape un micro exemple ce soir, pour te montrer comment faire pour communiquer entre plusieurs ViewControleurs.
Merci
Draken,
Alors là , chui grave intéressé, et je te remercie de consacrer de ton temps pour un débutant têtu comme moi.
À la bonne tienne
Bon, ça marche. Explications à venir demain. Je vais me coucher, là !
Salut Draken, je propose de créer un nouveau post.
Ce sera utile pour ceux que ça intéressent.
Je le commencerais avec un début de mes vagues connaissances issues des fichiers pdfs que je me suis constitués tout au long de ma formation POO au début de l'objectiveC.
Ce sera certainement plus pratique et rapide pour toi de remettre en question mes contradictions et inepties.
Et j'espère que cela servira à plusieurs collègues.
Nouveau post supprimé.
C'est bien compliqué ton truc, et inutilement verbeux. La théorie c'est pas mon truc. Je préfère la pratique et les petits exemples. D'ailleurs je ne vais pas vous montrer mon exemple, les 3/4 du code concernent la navigation dans UINavigationController, avec des gestures, ce qui n'a pas de rapport avec le sujet de la transmission de données entre UIViewControleur. Je l'ai juste fait pour le plaisir et pour tester la validité de quelques trucs.
Bon revenons au MVC. Comme c'est écrit partout dans la doc Apple, le MVC consiste à utiliser 3 objets : un Modèle, une Vue et un Contrôleur. C'est une vision simplifiée, en réalité un contrôleur n'est pas limité à un seul modèle de données. Il peut en utiliser plusieurs. De même un Modéle peut être utilisé par plusieurs controleurs. C'est ça le moyen de communication entre plusieurs UIViewControllers: avoir un objet en commun !
Pour mes tests, j'ai écrit un objet encapsulant une liste de noms, appelé UneListeDeNoms.
a
a
La structure de donnée interne est privée, on ne peut y accéder que par l'intermédiaire des méthodes de l'objet.
L'utilisation est simple :
a
a
J'ai donc une source de données, un objet modèle. Comment faire pour y accéder depuis différents UIViewControler ?
Je commence par créer un premier UIViewController, trés simplifié, avec un lien vers la source de données et un seul label. Le label est définit avec du code pour ne pas mêler Storyboard à cette histoire. Je me m'étend pas sur le sujet, il y a plusieurs manières de créer un label et le placer sur l'écran. A chacun sa méthode de prédilection.
aa
a
La variable listeDeNoms n'est pas définie pendant la création du UIViewController. C'est pourquoi elle est du type optionnelle (symbole ?) qui signifie pour Xcode : pour le moment cette variable contient nil. Elle prendra peut-être une autre valeur par la suite.
a
a
Juste après avoir créé un contrôleur de type UnePageViewController je lui indique sa source de données.
a
a
Et voilà , le contrôleur peut maintenant accéder à la liste de nom.
Dans mon exemple simpliste, le contrôleur se contente de lire le premier nom de la liste (index 0) et de l'afficher dans le label.
La méthode viewWillAppear() est une fonction standard du UIViewControlleur, appelée automatiquement avant chaque affichage de la vue. Je l'utilise pour actualiser le contenu de mes données, avant l'affichage.
Quand le système a besoin d'afficher la vue, il commence par interroger la liste de noms, ce qui garantit la cohérence de l'information, selon la logique MVC. Le Contrôleur demande les informations au Modèle avant de dessiner la Vue !
a
a
Il n'est pas très utile d'avoir plusieurs exemplaire du même contrôleur accédant à chaque fois au premier nom de la liste. C'est pourquoi j'ai ajouté une propriétée supplémentaire, pour "personnaliser" le contrôleur, en l'occurence un numéro de page.
a
a
a
J'ai aussi modifié la méthode viewWillAppear() pour qu'elle lise le nom correspondant au numéro de page.
a
a
Comme ça, il est possible de créer plusieurs pages différentes.
a
a
Il ne reste plus qu'à intégrer ces pages dans un conteneur, comme un UIPageViewController ou un autre mécanisme du même genre. Elles fonctionnent indépendamment les unes des autres, mais utilisent les données de la même source. Tu n'as pas besoin de transférer 300 données à chaque fois. Quand un UIViewController a besoin d'une information spécifique, il la demande à la listeDeNoms.
Une remarque importante : Le UIViewController n'a pas la moindre idée de la manière dont fonctionne la liste de noms, il sais juste comment communiquer avec. Les deux objets sont complètement indépendants. Cela permet de modifier l'origine des noms, sans toucher au code du contrôleur. Pour le moment c'est un tableau de String codé en dur. Mais cela pourrais provenir d'un fichier texte, d'un fichier récupéré d'un serveur ou d'une base de données comme CoreData.
Fin du premier acte.. Le post suivant sera consacré au transfert d'une information d'un UIViewController à un autre.
EDIT : J'ai modifié plusieurs fois cette page pour améliorer la présentation. Le forum élimine automatiquement les lignes vides autour des balises de code, ce qui oblige à ruser pour avoir une présentation lisible.
Acte 2 .. comment transférer une information d'un UIViewController vers un autre ?
Imaginons qu'un UnePageViewController quelconque ai besoin de changer le nom de la troisième personne de la liste. Il lui suffit de le demander à la listeDeNoms. Techniquement, le Contrôleur modifie le Modèle.
a
a
"Nicolas Tesla" devient alors "Groucho Marx" dans la liste de noms. Quand la troisième page de ton UIPageViewController sera affichée, le label contiendra "Groucho Marx" (célèbre comique du cinéma américain en noir et blanc, à la place de Tesla, l'inventeur du courant alternatif). Il y a bien eu transfert d'une information d'un UIViewControleur à un autre, en respectant le MVC.
Tu peux transmettre toutes sortes d'informations avec un modèle plus complexe (nom, âge, couleur des yeux, niveau de vision, etc..). Et rien ne t'empêche d'utiliser plusieurs modèles de données dans un même UIViewController sur le même principe. Comme par exemple, la liste des noms, la liste des dossiers médicaux et d'autres choses encore.
a
a
Une petite remarque technique : je peux modifier le contenu de la liste de noms parce que c'est un tableau mutable de String, défini avec var. Si j'avais utilisé let pour créer le tableau, il serait immuable et ne pourrais être modifié.
Un contrôleur de ton application, situé en dehors du mécanisme des pages peut aussi agir sur les données. Par exemple, on peut avoir une vue administrative, pour changer les infos civiles d'un client (nom, prénom, etc..). Il lui suffit pour ça d'avoir un lien vers la liste des noms. Pas besoin par contre, d'avoir un numéro de page, ni un lien vers le dossier médical.
a
a
On peut aussi avoir une liste des factures, non accessible depuis les UIViewControllers de la partie médicale de l'application.
a
a
Finish ! Des questions ?
Ton prédécesseur, plutôt ! J'écrivais des tutos avant même que tu ne découvres l'existence des ordinateurs. Mais tu seras toujours 100x meilleur que moi, parce que tu as l'informatique dans le sang, alors que de mon point de vue c'est juste un outil compliqué pour créer des choses (surtout des jeux vidéos en fait!).
Une compilation de mes meilleurs articles sur la programmation des jeux vidéo sur Atari ST, publiée en ... 1993 :
http://www.abandonware-magazines.org/affiche_mag.php?mag=5&num=1134&album=oui
Salut Draken,
ouaaaaaaah, vraiment génial ton truc. C'est très progressif et super documenté.
Donc, juste une petite question concernant mon cas.
Je vais avoir plein de photos, de labels, de champs texte dans toutes mes vues (en gros 8 groupes de 5 vues = 40 vues)
Tout sera rempli par l'utilisateur, puis sauvegardé dans le coreData.
Je comptais placer un maximum d'éléments dans le storyboard. Au vu de ton tuto, il me semble qu'il serait préférable que je fasse comme toi, c'est-à -dire construire des objets ainsi :
Qu'en penses-tu ?
D'autre part, je vais essayer de reconstruire ma parties UIPageViewController, en utilisant, sur tes conseils, à la place, un UINavigationController.
Mon erreur concernant le choix de UIPageViewController, est que j'ai voulu construire selon le même schéma que l'utilisateur verra (la hiérarchie des étapes et des sous-étapes).
Tu penses que c'est jouable tranquille ?
Je n'ai aucune inquiétude pour la gestion de mon UIPageControl (les petits dots)
En tout cas, je vais me mettre sur ton tuto, et essayer de reconstruire cet exemple en l'adaptant à mon projet, je pense que je le comprendrais encore mieux ces concepts fondamentaux :
et
Il faut que je fasse bien rentrer ce truc dans ma p'tite tête, car, j'en suis certain, je vais gagner du temps et ce sera beaucoup plus facile à retravailler dessus.
Un grand merci pour tout.
J'ai hâte de réaliser ces trucs et de te le montrer, ainsi, tes effort auront porté leurs fruits.
ça sous-entend que je peux quand même créer des objets type :
Et les remplir dans ces objets comme tu le fait sur ta liste de nom, mais avec des méthodes coreData du type :
en jouant sur indexPath, et sans aller plus loin dans mon code coreData déjà existant.
Tu es d'accord ?
Oui, et tu n'as même pas besoin d'un tableau dans ce cas.
Salut Draken,
après une semaine de galère, je reviens vers toi, car j'ai plusieurs problèmes qui s'enchainent.
Je ne te citerai que le premier, car je pense que la suite s'arrangera, lorsque je l'aurai résolu.
Une remarque :
Tu fais :
Et moi, je suis obligé de faire :
Et impossible de faire :
dans le viewDidLoad
J'ai tout essayé, les "?" les "!" - et ça, ça marche pas non plus
Mais ça ne pose pas de problème - Apparemment.
Ensuite, je crée mon objet (je te la fait simple)
Puis dans un :
Je met :
Et rien ne se passe. la function toto n'est pas appelée. Pas de println toto
T'as une idée ?
Merci d'avance
Pour commencer, quelle version d'XCode utilises-tu ? Au fait, il y a une mise à jour d'XCode 6.3 depuis hier soir.