SegmentedControl, TableView, UIView, MVC...
Bonsoir les amis,
J'ai un petit soucis de conception et de compréhension...
J'ai une UITableView dans un UITableViewController qui se charge parfaitement avec les données provenant d'une autre classe. Jusque là tout va bien.
Cette UITableView fait des push dans la méthode delegate adéquate comme ceci vers un autre controller de vue :
J'ai donc dans mon contentViewController des accesseurs (items, item) délcarés en propriétés.
Mon contentViewController fait son boulot avec ses outlets et affiche ce qu'il faut dans sa vue.
Maintenant, j'ai un navigationSegmentedControl dans cette vue pour passer directement d'un item de ma tableview à l'autre pour ne pas avoir à revenir à la tableView.
Je ne sais pas bien comment le gérer proprement et en "bon MVC" qui se respecte. Ce navigationSegmentedControl appelle l'action suivante :
En fait, je me pose des questions de logique. Le but du jeu est de récupérer l'index de l'item en cours d'affichage pour pouvoir afficher l'item suivant ou précédent.
Déjà l'index de cet item doit il être "setté" juste avant le push de la tableView ?
Y-a-t-il un moyen de le récupérer à partir de :
Plus grave : est-ce que je fais mon setItem au bon endroit dans la méthode delegate de la tableView ou bien devrais-je plutôt juste envoyer mon index depuis là -bas, puis récupérer le items array général depuis viewDidLoad de la vue contentViewController ? ET enfin récupérer le bon dico à partir de ces 2 infos dans viewDidLoad ?
Enfin, dois-je récupérer mon nib et le charger à chaque fois que je switche dans le segmentedControlAction ?
J'ai un petit soucis de conception et de compréhension...
J'ai une UITableView dans un UITableViewController qui se charge parfaitement avec les données provenant d'une autre classe. Jusque là tout va bien.
Cette UITableView fait des push dans la méthode delegate adéquate comme ceci vers un autre controller de vue :
<br />- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {<br /> <br /> // load the content view controller with its nib file<br /> ContentViewController *contentViewController;<br /> contentViewController = [[ContentViewController alloc]initWithNibName:@"ContentView" bundle:nil];<br /><br /> // send data to view controller<br /> int itemIndex = [indexPath indexAtPosition: [indexPath length] - 1];<br /> NSDictionary *currentItem = [NSDictionary dictionaryWithDictionary:[items objectAtIndex: itemIndex]];<br /> [contentViewController setItem:currentItem];<br /> [contentViewController setItems:items]; <br /> <br /> // load the view now !<br /> [[self navigationController] pushViewController:contentViewController animated:YES];<br /><br /> // release controller<br /> [contentViewController release];<br />}<br /><br />
J'ai donc dans mon contentViewController des accesseurs (items, item) délcarés en propriétés.
Mon contentViewController fait son boulot avec ses outlets et affiche ce qu'il faut dans sa vue.
Maintenant, j'ai un navigationSegmentedControl dans cette vue pour passer directement d'un item de ma tableview à l'autre pour ne pas avoir à revenir à la tableView.
Je ne sais pas bien comment le gérer proprement et en "bon MVC" qui se respecte. Ce navigationSegmentedControl appelle l'action suivante :
<br />- (IBAction)segmentedControlAction:(id)sender {<br /> switch([sender selectedSegmentIndex]) {<br /> case 1: <br /> {<br /> NSLog(@"1");<br /> }<br /> case 0:<br /> {<br /> NSLog(@"0");<br /> }<br /> }<br />}<br />
En fait, je me pose des questions de logique. Le but du jeu est de récupérer l'index de l'item en cours d'affichage pour pouvoir afficher l'item suivant ou précédent.
Déjà l'index de cet item doit il être "setté" juste avant le push de la tableView ?
Y-a-t-il un moyen de le récupérer à partir de :
<br />NSArray * viewControllers=[self.navigationController viewControllers];<br />
Plus grave : est-ce que je fais mon setItem au bon endroit dans la méthode delegate de la tableView ou bien devrais-je plutôt juste envoyer mon index depuis là -bas, puis récupérer le items array général depuis viewDidLoad de la vue contentViewController ? ET enfin récupérer le bon dico à partir de ces 2 infos dans viewDidLoad ?
Enfin, dois-je récupérer mon nib et le charger à chaque fois que je switche dans le segmentedControlAction ?
<br />ContentViewController *contentViewController;<br /> contentViewController = [[ContentViewController alloc]initWithNibName:@"ContentView" bundle:nil];<br />
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
(en fait je me suis inspiré du tuto de Philippe qui n'a pas l'air là ces temps-ci...) ;-)
N'hésitez pas si vous voulez que je développe un passage.
indexPath indiquant où il doit aller chercher ses infos dans la source.
Au moment où tu le push (suite à un clic sur un item dans la vue parente), tu fixes l'indexPath pour correspondre à la sélection que tu as faite.
Au moment où tu changes la sélection de ton SegmentedControl, il suffit d'adapter ton indexPath pour refléter le nouveau "chemin d'accès" des données pour qu'il aille chercher les infos du nouvel item (et de faire un reloadData ensuite bien sûr).
Ca te semble jouable vu ton archi d'appli actuelle ? (J'ai pas tout lu de ta description j'ai du mal à voir le truc dans son ensemble)
Je suis OK sur le fait d'envoyer l'indexPath.
Ce que je voulais savoir surtout c'est s'il faut envoyer le array de sources à la vue détaillée depuis le controller tableView au moment du push (et puis par la suite à chaque changement de segmentedControl ?), ou bien s'il faut l'absorber sur le TableViewController (avec un accesseur get ?) depuis la vue détaillée dans le viewDidLoad par exemple ?! C'est purement un problème objet là je crois.
Ou alors tu as des dataSource distincts (tu te sers de tes UITableViewControllers comme datasource de tes UITableViews), et dans ce cas passer tout le tableau de données à ton dataSource c'est pas méchant, car ça ne fera qu'un retain de plus dessus (donc juste augmenter son retainCount de 1), ça va pas en faire une copie non plus (parce que là par contre, faire une copie de tout ton tableau de données, côté utilisation mémoire ça serait bof !)
J'ai du mal à suivre là ...
En fait, j'ai 2 tableView gérées par 2 tableViewControllers qui doivent envoyer les infos à la même UIViewController.
Dans mon UIViewController, je peux récupérer la vue parente, donc la tableViewController grâce à navigationController. Je pensais donc pouvoir récupérer le datasource, mais ça m'oblige à faire un truc du genre :
J'ai l'impression que je suis vraiment à côté de la plaque... alors qu'il me paraà®t 100 fois plus simple de faire un [tableViewController items] pour récupérer directement mon array de items ?
Parce ce que moi ce qui m'intéresse, c'est mon tableau de ressources et non mon datasource qui ne m'est utile que pour la tableView et le tableViewController ?!
De toute façon, même en récupérant le dataSource, je ne sais pas si je peux récupérer mes items qui l'alimentent ?
Mais j'avoue que je trouve ça bizarre, enfin j'ai du mal à cerner ton architecture (t'as pas des captures d'interface ou un petit graphe OmniGraffle pour montrer le principe en plus parlant ?)... Que tu passes ton tableau de données entre tes divers dataSource (utilisant les mêmes données sources mais n'allant pas chercher au même endroit dans ton NSArray pour le même indexPath selon la tabeView à remplir), mais que tu cherches à "récupérer" ton tableau d'items ?
Enfin c'est p'tet une mauvaise interprétation de ma part, en fait j'ai l'impression que tu veux que tes TableViewControlleurs "récupèrent" ton tableau d'items, au lieu que tu leur passes ton tableau d'item à l'initialisation ? En fait c'est le "sens" dans lequel tu sembles vouloir "envoyer" tes infos qui me gène, tu sembles vouloir faire en sorte que tes TableViewControllers "demandent" le tableau des items... alors qu'on est sensé leur "fournir" et que c'est à eux que tes tableviews vont "demander" ensuite les données pour chaque cellule... donc quand toi tu dis que tu veux que tes tableViewControlleurs "récupèrent" ton tableau d'items, moi je dis plutôt que tu dois leur "fournir" à l'initialisation.
Voilà mon architecture.
J'ai un appDelegate, un xmlParser, 2 tableViewController et 1 UIViewController.
Ce qui nous intéresse ici :
un tableViewController qui envoie les infos détaillées à afficher à l'UIViewController.
Le tableViewController charge l'array d'items dans une tableView via les méthodes delegate.
Au moment du push, il envoie l'index de l'item que l'on doit visualiser. On était d'accord sur ce point.
La question principale de conception était la suivante : faut-il envoyer l'array d'items géré dans UITableViewController au moment du Push ou bien faut-il le récupérer dans le UIViewController en s'appuyant sur un accesseur get placé dans UITableViewController ?
Je m'oriente plutôt vers la seconde méthode d'autant plus que c'est plus logique vu qu'après, une fois dans le UIViewController, je vais jongler avec cet array d'items et l'index de l'item en cours d'affichage pour afficher les items précédents ou suivants par le segmentedControl.
Donc je pense que j'ai un peu la réponse à ma question, mais je voulais m'assurer que c'était pas trop mauvais niveau MVC vu que je débute sur l'iphone.
T'aurais pas pu dire dès le début que ta tvc1 était la liste des posts osx-dev, tvc2 la liste des messages du post choisi dans tvc1, et detailsVC l'affichage du post sélectionné ? J'aurais mieux compris dès le début :P ;D
Heu c'est justement l'emploi de ces termes qui me gêne là ... ça va pas dans ce sens là , mais dans le sens inverse normalement !! C'est pas le TableViewController qui charge l'array d'items dans la tableView, c'est la tableView qui demande les items au TableViewController. Et c'est pas via les méthodes de delegate, mais de dataSource.
Je sais pas si c'est ce que tu voulais dire mais que tu n'utilises pas les bons termes, ou si tu fais effectivement tout à l'envers... ou si c'est moi qui suis trop "exigeant" sur les termes et le vocabulaire à utiliser, mais justement on parle d'architecture, donc faut bien qu'on soit d'accord sur l'organisation des choses, de comment ça fonctionne et dans quels sens vont les messages !
Ca ok, au moment où tu crées l'instance de ton PostViewController, tu affectes l'index de l'item à visualiser à une variable d'instance de ce PostViewController avant de le pusher.
Oui, à mon avis le mieux c'est que ton PostsTableViewController (celui qui push ton UIViewController) soit le dataSource dudit UIViewController, et quand tu as besoin de connaà®tre les données à afficher dans ton PostViewController, ce dernier demande à son dataSource (au PostsTableViewController quoi) le texte pour le post n°index (où index est la variable d'instance du PostViewController que tu as initialisé avant de le pusher... et que tu as potentiellement modifié par la suite à l'aide de ton SegmentedControl)...
En faisant comme ça c'est propre, ça respecte le pattern du dataSource, et tu ne demandes que ce dont tu as besoin.
Après que le dataSource de ton UIViewController (donc en somme ton PostsTableViewController) fournisse d'un coup un NSDictionary représentant le post demandé (son titre, son auteur, son texte...), genre appeler une méthode du style "-(NSDictionary*)postInfoForIndex:(NSUInteger)index" sur ton dataSource pour récupérer toutes les infos du post index... ou qu'il fournisse " un peu plus à l'image d'un UITableViewDataSource " chaque item à la demande (donc tu appellerais "titleForPostIndex:", "textForPostIndex:" et "authorForPostIndex:" chacun son tour sur ton dataSource pour obtenir les informations), ça se vaut, à toi de voir...
Voilà pour le principe, du moins tel que je le vois
Alors j'en ai cassé une partie pour suivre tes consignes qui m'ont l'air plus "cocoa friendly".
Globalement, tu as compris ce que je voulais, même si les 2 tableView (topics et messages) sont au même niveau, et pushent vers la même vue détaillée, dont le datasource peut donc être soit les topics, soit les messages.
Maintenant ça bugue un peu, et je suis allé relire quelques discussions sur ce site sur les protocoles pour me rafraà®chir la mémoire.
Voilà le code d'interface de TopicsTableViewControllers où tout se passe bien.
Note que je préfère une méthode qui va chercher l'item (le dico) plutôt que chaque propriété. Le protocole s'appelle ItemsViewDataSource plutôt que PostViewDataSource tout simplement parce qu'il peut s'agir du datasource de topics ou de messages.
Maintenant, le code d'interface et d'implémentation de contentViewController. C'est bien lui qui doit implémenter la méthode du protocole non ?
Déjà si tu redéclares :
et
c'est pour rajouter le reloadData je pense, puisque ces 2 méthodes sont sensées être implémentées par défaut avec le synthetize.
Ensuite, je pensais que c'était contentViewController qui devait implémenter la méthode du protocole ItemsViewDataSource or TopicsTableViewController me la réclame en warning après cette ligne :
Donc je n'ai pas saisi où implémenter la méthode du protocole...
Exactement. Si j'avais pas mis ces méthodes, ça aurait marché... mais le changement d'itemIndex n'aurait pas provoqué de rechargement du contenu de ta ContentView automatiquement, c'est pour ça que j'ai réécrit ces 2 méthodes pour remplacer l'implémentation par défaut qu'il m'aurait fait sinon avec le synthesize.
Comme dit au début de ma réponse : c'est le dataSource qui doit implémenter le protocole de ItemsViewDataSource, pas le ContentViewController. Donc c'est TopicsTableViewController ou MessagesTableViewController (ou en fait les deux, si les deux peuvent être mis en dataSource de ton UIViewController). Puisque c'est eux qui seront les dataSource, donc c'est à eux que ton ContentViewController va demander les données... en appelant les méthodes du protocole sur ces classes qui seront ton dataSource.
J'ai rajouté une ou deux méthodes de protocoles dont j'ai besoin.
J'ai aussi bridé mon segmentedControl pour pas qu'il aille chercher des items du datasource qui n'existent pas.
Maintenant, je cherche à agir sur la propriété "selected" de chaque bouton du segmentedControl. Or je n'ai trouvé que : setEnabled:forSegmentAtIndex: dans la doc Apple.
Qu'est-ce qui correspond à : setSelected:forSegmentAtIndex: ?
Je n'ai rien vu non plus dans les classes parentes !