Vues avec n°d'identification [résolu]
Avant de me lancer dans un projet j'aimerai avoir vos avis d'expert sur la manière générale d'appréhender le tout.
Le projet consiste à avoir un certain nombre de fiches (avec une image en recto et un texte en verso). A partir d'une vue, on charge quatre de ces fiches (au hasard). Depuis là , on clique sur une des fiches pour en avoir le détail :
J'aimerai éviter de créer trop de vues inutiles. J'avais donc l'intention de créer des nib pour chaque fiche (avec recto/verso inclus). Ensuite, je pensais leur donner un n° d'identification et utiliser ce numéro pour les afficher et les tirer au hasard.
Est-ce que je suis dans le juste ? et est-ce que je peux afficher plusieurs de ces fiches (donc des vues) dans une autre ?
Je n'ai pas encore bien déterminé la méthode, mais je pense à long terme traduire les textes sur les fiches. Ces textes devraient-ils aussi être pris en compte dans la conception de départ ou puis-je m'en occuper plus tard ?
Je suis encore très au début, mais j'ai vu qu'en partant de ce qui existe (tutos - merci philippe - , exemple apple et autre) je partais pas dans la bonne direction. Heureusement, cela - et toujours grâce à ce formidable forum - m'a permis d'apprendre et ce n'est jamais perdu.
Le projet consiste à avoir un certain nombre de fiches (avec une image en recto et un texte en verso). A partir d'une vue, on charge quatre de ces fiches (au hasard). Depuis là , on clique sur une des fiches pour en avoir le détail :
J'aimerai éviter de créer trop de vues inutiles. J'avais donc l'intention de créer des nib pour chaque fiche (avec recto/verso inclus). Ensuite, je pensais leur donner un n° d'identification et utiliser ce numéro pour les afficher et les tirer au hasard.
Est-ce que je suis dans le juste ? et est-ce que je peux afficher plusieurs de ces fiches (donc des vues) dans une autre ?
Je n'ai pas encore bien déterminé la méthode, mais je pense à long terme traduire les textes sur les fiches. Ces textes devraient-ils aussi être pris en compte dans la conception de départ ou puis-je m'en occuper plus tard ?
Je suis encore très au début, mais j'ai vu qu'en partant de ce qui existe (tutos - merci philippe - , exemple apple et autre) je partais pas dans la bonne direction. Heureusement, cela - et toujours grâce à ce formidable forum - m'a permis d'apprendre et ce n'est jamais perdu.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Lorsque tu crées un XIB, tu positionnes tes objects (en général objets graphiques genre UIView, UIButton...) dans InterfaceBuilder, tu les connectes entre eux, puis tu sauves le XIB.
Dans ton code, à un moment donné (explicitement ou implicitement) tu vas demander le "désarchivage" du XIB. Cela aura pour effet d'aller "lire le contenu du XIB" et reconstruire ces objets, les positionner au bon endroit, et les interconnecter entre eux, le tout en une ligne de code.
Un peu comme si tu te préparais une arborescence de fichiers dans le Finder, une fois qu'elle était prête, tu la zippais... et quand tu avais besoin de recréer cette arborescence, t'avais plus qu'à désarchiver ton ZIP à l'endroit voulu, puis éventuellement rajouter 2 ou 3 fichiers à cette arborescence déjà préparée.
Donc si ta UIView représentant une fiche (appellons là UIFicheView, sous-classe perso de UIView) a une interface constituée de quelques titres statiques et d'un texte qui lui par contre est différent d'une fiche à l'autre, un XIB est tout à fait adapté. Mais attention à respecter le modèle MVC, je n'ai pas l'impression que tu aies la bonne vision (en particulier quand tu parles de tes craintes lors de la traduction)...
L'idée est donc de créer un XIB représentant l'interface (générique) que tu vas utiliser pour une fiche... on est bien d'accord, il n'y aura qu'un seul XIB, utilisé comme modèle pour toutes les instances de fiches que tu vas créer, même si tu as 30 fiches avec des contenus différents, chaque fiche aura une présentation commune, seul le texte de chaque fiche sera différent.
Donc dans ton "UIFicheView.xib", tu crées une UIView, y place quelques UILabels pour les textes génériques genre labels statiques "Titre :", "Contenu :", "Description", et des UILabels ou UITextView (selon ce que tu veux afficher et si tu veux du texte éditable etc) pour recevoir les contenus de tes fiches... mais tu laisses ces contenus vides, on modifiera ces contenus par code pour chaque ficher créée puisque chaque fiche aura un contenu différent !
Il suffit donc de prévoir un IBOutlet vers ces zones de texte (UILabels ou UITextView) qui afficheront le contenu de ta fiche, et on pourra alors remplir leur contenu par code.
Après, le reste c'est côté modèle que ça se passe : tu peux avoir un tableau de contenus, et quand tu veux créer 4 fiches au hasard, tu crées 4 UIFicheView chacune à partir de ton XIB "UIFicheView.xib", ce qui va te créer des interfaces de fiches toutes préparées... et il te suffira de les remplir. Après le côté "je choisis 4 fiches au hasard" c'est dans la partie modèle, dans ton tableau de contenus, que tu vas aller récupérer 4 textes au hasard, et remplir chacune de tes 4 UIFicheView par un de ces 4 texte, pour qu'ainsi chacune de tes 4 UIFicheView, basées sur le même modèle d'interface (ton XIB), ait un contenu différent.
Ne va en tout cas surtout pas créer un fichier XIB par fiche que tu veux créer dans ton app !! Tu ne t'en sortirais pas !!
Donc je vais travailler la création de tableaux... et je reviendrai certainement vous embêter.
Bon, même si c'est plus long, j'ai pu m'en sortir avec du MVC sans tableau. J'ai intégré sans souci mes donnée dans un XIB comme ceci :
Le problème qui reste est que je fais 4 tirage et que je voudrai enlever le cas déjà tiré. Par exemple, si a fiche 5 sort au premier choix, qu'elle ne puisse plus sortir au deuxième.
Peux-tu nous dire exactement ce qui t'embête avec les tableaux, ce que tu n'arrives pas à cerner ?
Un tableau en Cocoa peut contenir n'importe quel type de données (n'importe quel type d'objet plus exactement). A noter aussi que quand tu mets un objet dans un NSArray, cet objet est retenu par le tableau (le tableau envoie un retain implicite à l'objet) et quand tu enlèves cet objet du tableau (ou quand le tableau est "releasé"), le tableau envoie un release implicite au tableau. Enfin, les NSArray sont des tableaux constants c'est à dire qu'on ne peux pas leur ajouter ou supprimer des éléments, alors que les NSMutableArray on peut modifier leur contenu même après leur création.
Après, il suffit de les utiliser comme toute autre classe Cocoa : Voilà pour le principe de base.
Après, si tu fais ce tirage à plusieurs moments dans ton appli (et pas juste une seule fois à l'initialisation), ça peut être plus propre de créer ton tableau de titres au tout début (dans le "init" de ta classe typiquement, et tu fais le release de ce tableau dans le "dealloc"), et au moment du tirage, de demander une copie (mutableCopy) de ce tableau (pour pouvoir faire les tirages en enlevant les éléments piochés de cette copie au fur et à mesure, sans alterer le tableau complet original), faire les tirages, et relâcher cette copie altérée à la fin des tirages.
Comme ça tu gardes à tout moment ton tableau de titres complet avec tous les titres dedans, et ne travaille que sur une copie que tu modifies pour faire tes tirages, sans altérer le tableau complet dont tu auras besoin si tu veux faire un nouveau tirage sur l'ensemble des fiches.
Voilà , déjà je te laisse faire des tests en utilisant juste le titre de tes fiches (et laissant de côté "texte" et "image" pour l'instant), pour voir si tu arrives à faire fonctionner le code que je t'ai livré et si tu as des questions. On rajoutera la gestion de "texte" et "image" ensuite une fois la première partie maà®trisée.
Ensuite vu le warning tu essayes d'affecter un integer à une variable qui est de type pointeur. Tu n'aurais pas déclaré idx comme "int* idx" au lieu de "int idx" par hasard ?!
J'ai maintenant une erreur au niveau du simulateur : Program received signal: “EXC_ARITHMETICâ€. et semble bloquer sur la fonction random ?
Je sais pas moi, mettre des NSLog pour isoler le moment où il crash (et voir via ces NSLogs si les valeurs que tu obtiens aux étapes intermédiaires sont cohérentes aussi), remplacer random() par une valeur fixe quelconque (le temps des tests) pour voir si c'est bien random qui pose problème ou pas, mettre des breakpoints... tout ça quoi.
[edit] : Tout est rentré dans l'ordre. J'ai même réussi à faire un tableau avec des images. Une petite dernière question pour la route si j'ose : Comment faire pour lier tous ces éléments. C'est à dire que lorsque je tire le titre 5, l'image apparaisse ?
Pas mal, pour un débutant hein ?... enfin je pense que j'ai tout bon, parce que ça me semble bien fonctionner. J'ai ajouté les numéros d'index pour que tous mes éléments soit classés dans le même ordre et réutiliser le idx pour tirer les différents éléments au même index.
Je me demandai encore quelque chose à ce propos. Disons que dans mon cas j'affiche parfois le textePour et parfois le texteContre sur ma fiche. Dans le code je tire tout de même les deux textes pour les supprimer ; mais comme ils sont indexés d'une manière fixe, ça devrait marcher aussi si je ne le tire pas je pense.
Yes !!
A priori ta boucle for(int i=0;i<4;i++) va planter : au deuxième passage (i=1) , il n'y a plus rien dans tableauTitres, le idx est donc random()%0 ! mais bon si tu as prévu de mettre 4 objets dans chacun des NSArray, cela devrait rouler.
Plutôt que des mettre du code un peu peu long les 4*4 insertObject: atIndex: peuvent être remplacés par
Pour le "initWithObjects:..." au lieu du "insertObject: atIndex:" je ne peux que "plussoyer" comme on dit, c'est en effet plus propre. Et sinon MAGE même si tu préfères créer ses éléments dans des lignes de code séparées, je te conseille d'utiliser la méthode "addObject" (qui va rajouter l'objet à la fin du tableau) plutôt que "insertObject: atIndex:", d'une part parce que si Cocoa sait que tu veux insérer l'objet à la fin, il va le faire plus rapidement que s'il a à chercher l'index (bon ok côté gain de temps c'est vraiment plus que risible mais bon) mais surtout parce que c'est plus simple, tu n'as pas à spécifier l'index, au risque d'oublier de l'incrémenter au gré d'un copier/coller de lignes...
Bon ton code est propre et en effet ça doit faire ce que tu veux.
Ceci dit je te propose dans les posts suivant d'aller plus loin, dans un but pédagogique mais aussi pour te faire aux bonnes pratique courantes en Cocoa :
(1) Prochaine étape : regrouper tes données dans un NSArray de NSDictionary plutôt que d'avoir tout plein de NSArrays
(2) Etape suivante ensuite : définir tes données (donc ton NSArray de NSDictionary) dans un fichier plist, et non pas en dur dans le code, ce qui d'une t'évitera un gros paquet de lignes de code juste pour remplir tes tableaux, et permettra en plus de séparer le code (la logique de fonctionnement) des données dans un fichier séparé (et plus simple pour la traduction à terme) !
Evolution n°1 : Utiliser un NSArray de NSDictionary
L'idée c'est qu'au lieu d'avoir N tableaux (tableauTitres, tableauImages, tableauTextePour, tableauTexteContre, ...), tu n'en aies qu'un seul, qui au lieu de contenir une chaà®ne, va contenir un NSDictionary. Et chaque NSDictionnary sera constitué de N clés, qui seront "titre", "image", "textePour", "texteContre", et donc la valeur associée sera bah le contenu correspondant.
Ainsi, au lieu d'avoir : Tu auras :
Voilà pour l'idée, je te laisse coder ça si cette solution te séduit.
Ce n'est qu'une alternative possible et pas obligatoire hein (car ta solution à toi marche très bien aussi), mais cette solution a l'avantage d'éviter de démultiplier les tableaux (et d'avoir du coup 36 variables d'instance), et d'être sûr de la consistance (alors qu'avec plusieurs tableaux si jamais tu ajoutes une entrée dans un des tableaux et que tu oublies d'ajouter les infos correspondantes dans l'autre, ça va tout te décaler... et faut être sûr que les éléments correspondants soient aux mêmes indices pour que ça marche... avec un NSArray de NSDicos t'as pas ce pb)...
... Et puis si tu adoptes ce modèle plutôt que plein de NSArrays, ça facilitera le passage à l'évolution n°2
Je comprends bien l'idée de mettre tout dans un dictionary et d'en sortir les éléments par ligne, mais c'est justement parce que je n'arrive pas à tirer un code de la doc que j'avais laissé tomber les tableaux.
Je vais encore me faire taper sur les doigts, mais est-ce que je suis dans le juste avec ça pour créer mon dico ?
ps: le code avec "initWithObjects..." plantouille complètement. Je vais travailler l'histoire et je vous redonnerai des nouvelles
Le but est de créer un dictionnaire pour chaque fiche... donc d'appeller une des méthodes dictionaryWith... par exemple que propose NSDictionary, pour créer ce dictionnaire. Comme tu appelles "arrayWithObjects:" pour créer ton tableau. En tout cas pas en surchargeant (redéfinissant le code de) la méthode dictionaryWithObject:forKey: !
Sinon pour le reste du code (celui que tu as mis à l'intérieur des accolades quoi), faut bien que tu aies la vision que chaque fiche va être représentée par un NSDictionary. Par exemple la fiche 0 correspondra à un dictionnaire de ce genre : . Après, tu peux créer un NSArray (tableau) contenant toutes tes fiches comme objet (chaque case du tableau est une fiche, représentée par un NSDictionary tel que décrit plus haut)
Donc le code ça serait plutôt du genre, au début pour créer chacune de tes fiches : Ou une autre façon de faire tout aussi valable : A la fin de ce genre de code (que soit la variante choisie (parmi ces 2 là ou d'autres ayant le même résultat), tu as un NSDictionary par fiche... qu'il ne suffit plus que de regrouper dans un tableau : Et à la fin de tout ça, si tu veux récupérer toutes les infos de la fiche n°3, tu fais [tt]NSDictionary* maFiche3 = [mesFiches objectAtIndex:3];[/tt]. Et si tu fais un NSLog de ce résultat, tu retrouves le dictionary dont je t'ai donné la tronche plus haut... Après une fois que tu lui a demandé la fiche voulue et récupéré ce résultat, tu peux demander ce que tu veux à propos de la fiche (quand son titre, quand son imageName, ...) en interrogeant le NSDictionary récupéré et la décrivant, avec [tt]objectForKey:@titre[/tt] ou du genre.
Je me pose juste la question de l'utilité de:
Car la fonction de tirage vient de
ça fonctionne d'ailleurs sans.
[tt]Remarque: j'ai mis i<19, car j'ai 22 fiches. Je pensais qu'il fallait mettre i<20, mais ça plantait.[/tt]
A priori avec 22 fiches tirées une par une il faut mettre ni i<19 ni i<20 mais i<22 ?
J'ai compris où ça plante. Puisque j'enlève une ligne à chaque tirage, j'ai bien 22 au premier, mais 21 au deuxième, 20 au troisième et 19 au quatrième... et comme je tire quatre fois.
Enfin où tu as cette boucle "for" en fait...
Mais ça c'est quand tu fais ton random(), et dans ce cas il faut utiliser [tableauFiches count] et pas mettre un nombre comme 19 ou 22 en dur.
Comme ça déjà c'est plus logique/lisible*, en plus quand au fur et à mesure des tirages le compte change, ça correspond toujours, et en plus si un jour tu rajoutes des fiches dans ton tableau tout se fera toujours tout seul tu n'auras rien à changer
*En informatique on parle parfois de "magic number", pour indiquer dans du code des nombre qui sont là dans le code "comme par magie" mais on se demande d'où ils viennent, plus exactement si on lit le programme d'un autre, quand on tombe sur un nombre dont on ne sais pas d'où il sort et qu'on capte pas pourquoi l'auteur du code a mis cette valeur.
Il faut mieux dans ce cas à la limite utiliser des constantes, ou un appel explicite à une fonction (c'est le cas pour toi d'autant que cette valeur de count change), ou laisser le calcul décomposé de la valeur, genre 22-4+1 (ou mieux kNombreFiches-kNombreTirages+1) plutôt que de mettre un 19 dont on ne sait d'où il sort...
En tout cas dans ton cas je vois pas ce que tu fais avec le "19" dans ton "for"... ou alors on parle pas du même "for" (pas la boucle qui répète un tirage autant de fois... que tu veux faire de tirages, mais une autre boucle for) mais dans ce cas explique nous de laquelle tu parles :P Et sinon si c'est pour ton random faut garder [tableau count] vu justement qu'il change !
A force de modifications, j'ai perdu quelques concepts en route.
Très intéressante l'explication du "magic number".
Parce que c'est vrai que c'est pas super de devoir se balader dans le code
Pour créer le fichier Plist en question, rien de plus simple :
Rajoute 3 autres entrées, pour "imageName", "textePour" et "texteContre" de la même manière.
Astuce : une fois la première fiche (premier NSDictionary) préparé, tu peux sélectionner la ligne du NSDictionary et, après avoir refermé le petit triangle, faire un copier/coller pour le dubpliquer. Comme ça tu n'aurais que les valeurs à changer pour les adapter aux autres fiches, mais les clés seront déjà remplies.
Et voilà . Tu t'assures que ton fichier plist est bien rajouté dans le dossier/groupe "Resources" à gauche de ta fenêtre Xcode, et ça devrait être bon. Le simple [tt]arrayWithContentsOfFile[/tt] va lire le contenu du plist et recréer toute la structure pour toi en une ligne.
Comme ça tu as toutes tes données regroupées dans le plist déjà , donc plus propre, et en plus ça sera plus simple ensuite pour traduire en plusieurs langues si nécessaire.
Tout me semblait très clair... mais je m'en sort pas tout à fait
J'ai un warning : Après quelques recherches je pensais avoir trouvé le problème par une déclaration dans le .h mais ça n'a rien résolu.
J'en profite pour vérifier deux idées :
1) Est-ce une bonne méthode de passer mon tableau en mutable
ou c'est inutile et je peux directement le créer au départ ?
2) pour ma localisation, j'avais :
qu'est-ce que je mets dans mon .plist ?
Il fallait donc utiliser la méthode d'instance "pathForResource:ofType:", à appeler sur le mainBundle, et non la méthode de classe du même nom à appeler sur la classe NSBundle elle-même (méthode... qui n'existe pas dans la doc de NSBundle en fait, y'a juste sa variante avec le paramètre "inDirectory", mais elle ne nous intéresse pas trop ici)
Il fallait donc lire : [tt][[NSBundle mainBundle] pathForResource:@Fiches ofType:@plist][/tt].
Sinon pour la localisation/traduction, c'est simple, tu ne vas plus créer une version de chaque chaà®ne contenue dans tes fiches pour chaque langue... mais carrément un fichier plist pour chaque langue.
Ainsi tu auras un fichier plist pour les fiches en français, dont le contenu sera intégralement en français, une variante de ce plist pour la localisation anglaise, où tout sera en anglais dedans, etc.
Pour cela, une fois que ton fichier "Fiches.plist" a été placé dans le groupe "Resources" (partie gauche "Groups & Files" dans la fenêtre Xcode) de ton projet, tu cliques sur le fichier plist, tu fais un Pomme-I dessus, et tu cliques sur le bouton "Make Localizable" (onglet "General). Cela va rendre ton plist traduisible, en t'associant de base la version actuelle du plist à la langue anglaise (English). Après si tu retournes dans l'onglet "General" de ce panneau d'infos sur le plist, tu peux rajouter des traductions pour es langues différentes en cliquant sur "Add Localization". A gauche du fichier "Fiches.plist" que tu as dans ton groupe "Resources" tu auras de plus un petit triangle te permettant de voir les variantes de ton fichier plist, une pour chaque langue. Il te suffit de choisir une des variantes (par exemple celle pour French) et de modifier alors les valeurs que tu as mises dans ton plist pour toutes les passer en français.
Sinon si tu veux pouvoir modifier tes fiches, tu peux créer un NSMutableArray directement normalement (faut s'assurer que tout soit "mutable" une fois chargé), et tu pourras ensuite si besoin réécrire sa version modifier sur le disque dans un (autre) plist. (Pas dans le plist d'origine car ce dernier est dans le "bundle" de ton appli donc non modifiable une fois l'appli compilée, mais il y a des répertoires adéquats pour ça si besoin, genre Application Support ou autre)
Alors j'avoue que j'avance grâce à tes précieux conseils, Aligator
J'ai dû par exemple enlever le sinon ça plantait. J'ai pas vraiment compris pourquoi, sauf que j'ai vu que d'après cette méthode, il autorelease.
Par contre, je ne sais pas si c'est la mise à jour récente en 3.0 ou aux changements que je viens de faire, mais les actions sur ces tableaux sont bien plus lentes et moins fluides. Cela donne une impression de lourdeur.
C'est un vrai poison cette v3... je suis vraiment déçu que ça soit la meme version que la GM distribuée aux développeurs... moi j'ai trouvé plein de bug sur la GM.. et surtout cette histoire de batterie qui se vide en 2 jours >:(