Architecture MVC: Comment gérer plusieurs vue

GreensourceGreensource Membre
mars 2009 modifié dans API UIKit #1
Bonjour, je suis face à  un petit souci de conception. Dans mon application, je vais avoir plusieurs vue à  gérer. La première qui englobe les autres (MainView), une seconde qui représente un plateau de case (BoardView) et enfin une dernière qui est une vue de status du jeu (statusBar).
Voici où en est rendu ma conception:
picture1trx.png
Pour l'instant j'ai donc un controller qui gèrent mes trois vue. Mais je ne suis pas sur que cela respecte MVC et soit très bien penser. Ne devrais-je pas avoir trois controller supplémentaire (un par vue) et avoir dans le controller principal une instance de chaque controller individuel?
Merci de vos lumières  :D

[edit] En fait mon problème repose sur la question suivante. Comment se passe la création d'un MVC? J'instancie un Controller qui instancie sa vue et son model ou bien l'instancie une vue qui instancie son controller qui instancie son model?
«1

Réponses

  • AliGatorAliGator Membre, Modérateur
    20:15 modifié #2
    En fait ça dépend comment tu gères les choses, mais je pense que les 2 approches sont valables.

    Personnellement j'aurais un ViewController pour la vue principale, au sens UIViewController, classe Cocoa fournie par Apple qui permet de gérer le chargement de la vue et ses sous-vues, le changement éventuel d'orientation de l'iPhone/iPod Touch, etc.

    Et j'aurais un contrôleur au sens plus MVC du terme, pas forcément sous-classe de UIViewController (mais plutôt une classe perso qui n'hérite de rien de particulier, enfin qui hérite de NSObject quoi) pour mon jeu, qui gère l'affichage du plateau et du statut. Ce contrôleur gérerait les clics dans les cases de ton plateau, pour demander alors au modèle de ton MVC ce qu'il doit en faire,, si c'est un coup valide dans le jeu, etc... et qui demanderait à  la vue de se mettre à  jour lorsqu'un coup est joué, ce qui implique une mise à  jour à  la fois du plateau et de ta vue de statut qui affiche par exemple les scores. Donc comme les 2 sont liés côté modèle/contrôleur, même si tu as des vues séparées pour le plateau et pour le "statut", moi je verrais qu'un seul contrôleur pour ça (au sens MVC donc, pas nécessairement héritant de UIViewController)

    Les UIViewControllers oui ça fait partie de la partie C du MVC, mais ce sont des classes Cocoa qui de mon point de vue sont plus faites pour gérer les vues qui prennent tout l'écran, pour gérer leur transition à  l'écran, genre rotation si on tourne l'iPhone, ou pour pousser une autre vue (presentModalViewController, pushNavigationViewController, ...).
    Oh bien sûr dans une appli simple ces UIViewControllers sont suffisants pour gérer aussi la partie Contrôleur de ta vue, genre recevoir les IBActions des éléments de ta vue, ou accéder par IBOutlets aux éléments de ta vue pour les modifier/mettre à  jour, et c'est un modèle de conception tout à  fait valable. D'ailleurs comme dit au début, tu peux tout à  fait garder ce modèle, qui est celui que tu as actuellement, pour ton appli, ça marche aussi, plutôt que de suivre mon autre suggestion d'avoir un contrôleur à  part pour plateau+statut, pour moi les deux sont viables. Après, c'est plus si ton contrôleur commence à  prendre de l'ampleur côté code que tu y verras plus clair en ayant séparé en plusieurs contrôleurs  ;)
  • GreensourceGreensource Membre
    20:15 modifié #3
    Ouais c'est plutôt une bonne idée je trouve, j'avais un peu penser à  ça aussi dans le sens où ma barre du bas n'allais pas faire des milliers de choses.
    Voilà  ce que ça donne (je vous épargne les model):
    picture1172245.png

    Ca fonctionne plutôt bien comme ça! J'ai quelques truc qui ne plaise pas à  l'initialisation mais globalement c'est satisfaisant. Merci
    Si juste pour dire ce qui me chagrine quand même  :)
    J'instancie mes deux vue: GWStatusView et GWBoardView dans GWMainView. Sauf que j'ai besoin pour cela du controller GWGameController, donc je me dit: "Bon bas je l'instancie ici aussi"
    Sauf que j'ai besoin des deux vue pour l'instancié, vous voyez le souci, j'ai une boucle de dépendance. Alors j'ai bidouiller, dans mes deux vue j'appelle un setter du controller et je lui set ma vue en contruction:
    GWBoardController* gameController = [[GWBoardController alloc] init];<br />boardView = [[GWBoardView alloc] initWithFrame:boardFrame controller:gameController];<br />statusView = [[GWStatusBar alloc] initWithFrame:statusFrame controller:gameController];
    

    - (id)initWithFrame:(CGRect)frame controller:(GWBoardController*)theController<br />{<br />&nbsp; &nbsp; if (self = [super initWithFrame:frame]) {<br />		myController = [theController retain];<br />		[myController setBoardView:self];<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return self;<br />}
    
  • AliGatorAliGator Membre, Modérateur
    20:15 modifié #4
    Et pourquoi tu ne demandes pas à  ton ViewController GWMainViewController d'instancier le GWGameController, ça me semblerais plus logique ? Et ça pourrait être aussi une idée de déléguer au GWGameController la création de GWStatusView et GWBoardView (quitte à  lui passer, dans son constructeur dédié que tu surcharges, un paramètre en plus qui serait la UIView "GWMainView" à  laquelle il doit rajouter ses propres views ?

    GWMainViewController --> crée ma GWMainView* mainView, puis crée un un GWGameController avec alloc + initWithMainView:mainView (initWihMainView qui serait une méthode créée par tes soins qui fait un [super init] puis crée et rajoute tes 2 subview à  mainView, mainView que tu n'as pas besoin de garder en mémoire dans le GWGameController ensuite d'ailleurs une fois les subViews créées)
  • GreensourceGreensource Membre
    20:15 modifié #5
    Oui tu as raison, ça semble plus logique. Je vais mettre ça en place.
  • GreensourceGreensource Membre
    20:15 modifié #6
    Je ré-ouvre ce sujet car j'en suis à  une autre étape mais le titre correspond toujours très bien :P
    Donc j'ai bien réussi à  gérer plusieurs View dans une View principale, pas de souci!

    Maintenant je voudrais, lorsque l'on lance l'application arriver sur un Menu avec par exemple trois bouton: nouvelle partie,préférences,à  propos.
    à‰videmment lorsque l'on "toucherais" un bouton on changerais la vue. Voilà  où se trouvent mes interrogations.

    Pour l'instant j'ai fait un xib de type Application XIB. J'ai mis mes boutons et ajouter un UIViewController à  la UIWindow pour gérer ces boutons justement. Mon souci c'est pour gérer mes fichier xib en fait.

    Pour me faire la main j'ai créer un faux projet qui a un menu ainsi qu'un vue bleu et une vue verte (construit dans deux fichier xib). Mais primo je ne sais pas comment initialiser mes fichier xib?
    Visiblement le premier est désérialiser automatiquement parce que c'est écrit dans le .plist. Mais pour mes xib greenView.xib et blueView.xib?
    Je pensais le faire dans le MenuViewController, dans la méthode:
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    

    Mais bizarrement elle n'est pas appelé?

    Donc là  je suis un peu pommé, je vais retourné voie dans la doc, mais malheureusement tout ce que je trouve comme info c'est à  propos des NavBar ou bien des TabBar et c'est pas vraiment ça que je veux. C'est le même fonctionnement mais pas la même interface...
  • Philippe49Philippe49 Membre
    mars 2009 modifié #7
    dans 1238189778:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    

    Mais bizarrement elle n'est pas appelé?

    Si tu as mis les viewControllers dans le nib, cette méthode n'est pas appelée.

    dans 1238189778:

    Donc là  je suis un peu pommé,

    Manifestement t'es un fan de Mac : pommé <--> paumé




    Quand tu tapes sur un de tes boutons, cela devrait appeler une (IBAction).
    Dans l'IBAction d'a propos par exemple, tu mets comme code

    if(!aboutViewController) {
       aboutViewController=[[AboutViewController alloc] initWithNibName:@about bundle:nil];
       // + autres initialisations
    }

    // tu changes la vue  : [l'ancienne removeFromSuperview] ,  [...... addSubview:la nouvelle]
    // tu peux animer avec CATransition
  • GreensourceGreensource Membre
    mars 2009 modifié #8
    Citation de: Greensource le Hier à  22:36
    Donc là  je suis un peu pommé,

    Manifestement t'es un fan de Mac : pommé <--> paumé

    Arf, je suis grillé :P D'un je suis une buse en Français (mais je me soigne ^^) et oui je suis un bon fan d'Apple aussi :)

    Sinon merci je vais essayer de mettre ça en place! Mais du coup j'ai une question à  propos de:
    Si tu as mis les viewControllers dans le nib, cette méthode n'est pas appelée.

    Comment je fait dans ce cas pour configurer des trucs avec du code si aucune méthode n'est appelé?

    [edit] Bon j'ai encore du chemin à  faire...
    Je remarque que je ne sais pas bien à  quel fichier je dois attacha file's owner, et du coup ça ne dois pas pouvoir marcher à  mon avis! D'autre par, quand, dans IB, je met mon ViewController, il attache tout seul l'Outlet view à  View Alors même que je n'ai pas encore mis de vue! Et lorsque j'en met une et que j'e l'attache à  l'Outlet view et bien du coup j'ai un warning "multiple".
    Je me replonge dans la doc!
  • Philippe49Philippe49 Membre
    mars 2009 modifié #9
    dans 1238228101:

    Mais du coup j'ai une question à  propos de:
    Si tu as mis les viewControllers dans le nib, cette méthode n'est pas appelée.

    Comment je fait dans ce cas pour configurer des trucs avec du code si aucune méthode n'est appelé?

    Je ne comprends pas . Si l'utilisateur n'intervient pas, pourquoi la vue changerait ?
    • Tu veux précharger les vues ==> tu choisis une méthode de ton code viewDidLoad: par exemple, ou viewDidDisplay:  ou awakeFromNib (moins classique sur iPhone) et tu y mets ton code initialisant.
    • Tu veux faire défiler tes vues à  l'écran ==> utiliser un NSTimer pour périodiquement changer la vue en simulant le "touch" de l'utilisateur.
    • Tu laisses l'utilisateur piloter ==> méthode ci-dessus via les IBAction
  • Philippe49Philippe49 Membre
    20:15 modifié #10
    dans 1238228101:

    Je remarque que je ne sais pas bien à  quel fichier je dois attacha file's owner,

    Le File's owner doit avoir pour classe celle du code correspondant.
    Pour ton nib "About.xib" par exemple, File's Owner prend pour classe AboutViewController (inspecteur--> Identity)
    Ne pas oublier de connecter l'outlet view dans IB

    dans 1238228101:

    D'autre par, quand, dans IB, je met mon ViewController, il attache tout seul l'Outlet view à  View Alors même que je n'ai pas encore mis de vue!

    ?
  • GreensourceGreensource Membre
    20:15 modifié #11
    dans 1238231206:

    Le File's owner doit avoir pour classe celle du code correspondant.
    Pour ton nib "About.xib" par exemple, File's Owner prend pour classe AboutViewController (inspecteur--> Identity)
    Ne pas oublier de connecter l'outlet view dans IB

    D'accord, mais c'est étrange, le file's owner et le ViewController on alors le même fichier associé?

    dans 1238228101:

    D'autre par, quand, dans IB, je met mon ViewController, il attache tout seul l'Outlet view à  View Alors même que je n'ai pas encore mis de vue!

    Une images valant mieux qu'un long discours:

    picture1txw.png
    Tu vois c'est "grisé" l'Outlet view, et c'étais comme ça dès le départ.

    Une petite question supplémentaire, dans mon ViewController j'ai déclarer un Outlet myView, mais je crois comprend qu'en fait c'est déjà  intégrer à  UIViewController non? La property view c'est déjà  ça je suppose?

    Sinon je veux en effet que ça soit l'utilisateur qui change la vue en "touchant" les boutons du menu.
  • Philippe49Philippe49 Membre
    20:15 modifié #12
    dans 1238234392:

    D'accord, mais c'est étrange, le file's owner et le ViewController on alors le même fichier associé?

    Le File's Owner n'est qu'un proxy pour le désarchivage. Tu peux considérer qu'il n'y a qu'un seul view controller de créé.
  • Philippe49Philippe49 Membre
    mars 2009 modifié #13
    dans 1238228101:

    D'autre par, quand, dans IB, je met mon ViewController, il attache tout seul l'Outlet view à  View Alors même que je n'ai pas encore mis de vue!
    Une images valant mieux qu'un long discours:


    Effectivement, l'image est parlante ! Il ne faut pas mettre de GreenViewController dans ton GreenView.xib. C'est File's Owner qui sert de proxy entre le GreenViewController créé dans le code, et le matériel défini par le xib.


    dans 1238234392:

    Une petite question supplémentaire, dans mon ViewController j'ai déclarer un Outlet myView, mais je crois comprend qu'en fait c'est déjà  intégrer à  UIViewController non? La property view c'est déjà  ça je suppose?

    Oui

    dans 1238234392:

    Sinon je veux en effet que ça soit l'utilisateur qui change la vue en "touchant" les boutons du menu.

    Il faut alors déclarer une méthode -(IBAction) presentGreenView:(id)sender; et la connecter comme action du bouton dans le xib principal. Le principe du code est quelques posts plus haut.
    Si tu veux présenter la vue par une transition , tu as un exemple sur cette page ou tu regardes CATransition et les codes Apple qui vont avec.

  • GreensourceGreensource Membre
    mars 2009 modifié #14
    dans 1238234826:

    Effectivement, l'image est parlante ! Il ne faut pas mettre de GreenViewController dans ton GreenView.xib. C'est File's Owner qui sert de proxy entre le GreenViewController créé dans le code, et le matériel défini par le xib.

    Ah ok, je m'étais déjà  poser la question et je ne comprenais pas pourquoi. Merci!

    dans 1238234826:

    Il faut alors déclarer une méthode -(IBAction) presentGreenView:(id)sender; et la connecter comme action du bouton dans le xib principal. Le principe du code est quelques posts plus haut.
    Si tu veux présenter la vue par une transition , tu as un exemple sur cette page ou tu regardes CATransition et les codes Apple qui vont avec.

    Arrr je vais m'arracher les cheveux! Je viens de faire une manip': avant dans mon xib principale j'avais ma UIWindow dans laquelle j'avais directement mis mes boutons. Maintenant je me dit que pour faire les transitions, la UIWindow serais la superView et que donc il me faudrait mettre les boutons dans une UIView contenue dans la UIWindow. Sauf que maintenant les boutons refusent se faire une connexion avec mon IBAction! Je comprend pas mais d'un coup il re-veut bien??? à‰trange car je ne crois pas avoir modifié quoi que ce soit  ::)
  • Philippe49Philippe49 Membre
    20:15 modifié #15
    Sans doute une synchronisation ou un enregistrment des fichiers qui s'est faite.

    Autrement tu peux manipuler l'arborescence des vues (les faire changer de niveau, et d'ordre) dans le xib en le mettant  en choisissant le view-mode "Outline View" (mode fichier arborescent classique) 
  • GreensourceGreensource Membre
    mars 2009 modifié #16
    Bon alors j'ai un peu re-découper les choses. J'ai lu dans la doc d'Apple qu'un des erreurs étais de vouloir mettre plein de chose dans un xib. J'en ai donc créer 4:
    • Un pour la window, celui charger au départ: MainWindow.xib
    • Un pour le menu: Menu.xib
    • Un pour la vue Verte: GreenView.xib
    • Un Pour la vue Bleue: BlueView.xib

    Donc si je saisi bien les concepts, mon appli se lance, le fichier xib noté dans le .plist est chargé -> ça m'instantcie mon AppDelegate puis appelle la méthode:
    - (void)applicationDidFinishLaunching:(UIApplication *)application
    C'est ici que je dois dire ce que je veux afficher. Moi je fait:
    [window addSubview:[menuController view]];<br />    [window makeKeyAndVisible];
    

    Malheureusement, la vue du menuController ne s'affiche pas.
    Je viens de penser qu'il fallait surement d'abord désarchiver le xib Menu.xib  :P
    Je vais essayer...

    [edit] c'était bien ça! Désoler du post inutile du coup ^^
  • Philippe49Philippe49 Membre
    20:15 modifié #17
    dans 1238237030:

    Bon alors j'ai un peu re-découper les choses. J'ai lu dans la doc d'Apple qu'un des erreurs étais de vouloir mettre plein de chose dans un xib. J'en ai donc créer 4:
    • Un pour la window, celui charger au départ: MainWindow.xib
    • Un pour le menu: Menu.xib
    • Un pour la vue Verte: GreenView.xib
    • Un Pour la vue Bleue: BlueView.xib


    Tu as réinventé là  le template View-Based Application , et tu as eu bien raison.
    (Tu peux vérifier en créant une application sur ce template, si ta construction est semblable ... )


    dans 1238237030:

    Donc si je saisi bien les concepts, mon appli se lance, le fichier xib noté dans le .plist est chargé -> ça m'instantcie mon AppDelegate puis appelle la méthode:
    - (void)applicationDidFinishLaunching:(UIApplication *)application
    C'est ici que je dois dire ce que je veux afficher. Moi je fait:
    [window addSubview:[menuController view]];<br />    [window makeKeyAndVisible];
    

    Malheureusement, la vue du menuController ne s'affiche pas.

    Regarde comment il font dans le template. Tu dois simplement avoir oublié de définir le xib dans l'inspecteur pour ton MenuController.
  • GreensourceGreensource Membre
    20:15 modifié #18
    dans 1238237407:

    Tu as réinventé là  le template View-Based Application , et tu as eu bien raison.
    (Tu peux vérifier en créant une application sur ce template, si ta construction est semblable ... )

    Ouais c'est bien ce qu'il me semblais ^^
    Mais j'ai besoin de choses spécifique il me semble pour mon application, par exemple je ne veut pas de TabBar ni de NavBar. Donc je me disais que c'était mieux ainsi.
    Question, on peut enregistrer ses propres templates du coup?

    dans 1238237407:

    Regarde comment il font dans le template. Tu dois simplement avoir oublié de définir le xib dans l'inspecteur pour ton MenuController.

    J'ai trouver entre temps, c'était juste que n'avais pas créer mon controller via le xib dans mon code.

    Bon du coup ça marche bien et je comprend beaucoup mieux le fonctionnement d'IB! Merci Philippe, tu m'as bien aidé!

    Il me reste un petit truc bizarre, un restant de ma vue UIWindow qui traine en bas:
    picture2rpm.th.png
    J'ai mis un fond rouge pour différencier mes vues. Je me suis dit que ça devais venir du fait qu'il n'y a pas de statusBar dans ma vue Verte, mais non, même quand j'en ajoute, ça ne change rien...
  • Philippe49Philippe49 Membre
    20:15 modifié #19
    dans 1238238968:

    Question, on peut enregistrer ses propres templates du coup?

    Oui Psychoh13 nous avait fait un post là -dessus décrivant le principe.
    En gros il faut mettre le template au bon endroit de manière à  ce que XCode le repère au lancement.

    dans 1238238968:

    Il me reste un petit truc bizarre, un restant de ma vue UIWindow qui traine en bas:
    J'ai mis un fond rouge pour différencier mes vues. Je me suis dit que ça devais venir du fait qu'il n'y a pas de statusBar dans ma vue Verte, mais non, même quand j'en ajoute, ça ne change rien...

    Oui c'est un détail embêtant qui apparaà®t de temps en temps , dû au redimensionnement automatique des vues. Les solutions peuvent être :
    • Réglages dans le panel Size de l'inspecteur
    • Le réglage status bar , top bar , bottom bar
    • La bonne construction de l'architecture, qui décide du redimensionnement automatique
    • En dernier recours, redimensionner à  la main dans le code
  • GreensourceGreensource Membre
    20:15 modifié #20
    Voilà  j'ai fini! :adios!: Enfin ça fonctionne comme je veux, c'est déjà  ça!
    J'ai juste une interrogation à  propos de mon architecture. Pour pouvoir revenir au menu j'ai créer dans mes deux controller de vue couleur (GreenController et BlueController) un pointeur vers le MenuController. Du coup quand je veux revenir au menu, j'ai un bouton qui appelle l'action suivante:
    - (void)toMenu<br />{<br />	UIView* theSuperView = [[[self view] superview] retain];<br />	[[self view] removeFromSuperview];<br />	[theSuperView addSubview:[superController view]];<br />	[theSuperView release];<br />}
    

    Et donc j'ai des quelques doutes sur cette architecture, notamment est-ce normal d'avoir les contrôleurs qui se connaisse les un les autres? Et d'ailleurs, plus généralement dans MVC comment dois ton faire les liens? Plutôt les Controlleurs qui se connaissent, plutôt les Vues? Ou bien ça dépend de ce qu'on veut faire? Ca fait plusieurs fois que je me pose la question mais je n'ai jamais vraiment trouver une réponse.

    Ps: Je joint le code qui marche, j'en ferais pis être un Tuto si vous pensez que ça peut-être intéressant pour le gens qui comme moi voudrais ne pas dépendre de NavBar et TabBar.
  • Philippe49Philippe49 Membre
    avril 2009 modifié #21
    dans 1238244582:

    - (void)toMenu<br />{<br />	UIView* theSuperView = [[[self view] superview] retain];<br />	[[self view] removeFromSuperview];<br />	[theSuperView addSubview:[superController view]];<br />	[theSuperView release];<br />}
    


    Ok, simplement le couple (retain de la première ligne / release de la dernière) est inutile pour deux raisons :
    • La fameuse superview est retenue par ailleurs, donc il n'y a aucune raison qu'elle soit désallouée entre temps.
    • L'intérieur du code de ta méthode est réalisée dans le même cycle de RunLoop, ce qui fait q'il n'y a aucune chance qu'un release vienne désallouer la superview.  
  • Philippe49Philippe49 Membre
    20:15 modifié #22
    dans 1238244582:

    Et donc j'ai des quelques doutes sur cette architecture, notamment est-ce normal d'avoir les contrôleurs qui se connaisse les un les autres? Et d'ailleurs, plus généralement dans MVC comment dois ton faire les liens? Plutôt les Controlleurs qui se connaissent, plutôt les Vues?

    Pour moi, ce sont les contrôleurs qui doivent se connaà®tre et dialoguer en priorité.

    dans 1238244582:

    Ps: Je joint le code qui marche, j'en ferais pis être un Tuto si vous pensez que ça peut-être intéressant pour le gens qui comme moi voudrais ne pas dépendre de NavBar et TabBar.

    Très utile pour les autres, et .. pour toi également.
    (Ceci dit dans un tel tuto effectue une comparaison avec le template View-Based Application)
  • GreensourceGreensource Membre
    20:15 modifié #23
    Ah cool pour la superView, je ne savais pas.
    Bonne idée en effet de comparer avec le Template, je vais essayer de voir ça.

    Sinon je viens d'appliquer tout ça à  mon vrai projet. Ca marche nickel, comme quoi le conseil que j'avais vu sur Cocoa.fr comme quoi il était intéressant de faire des mini projet pour tester des nouveau truc est une très bonne idée.
    Mais j'ai un warning dont je n'arrive pas à  me débarrasser. Dans mon xib j'ai mis une UIImageView en tant que view de mon controller. Seulement ça mon code ne le sais pas:
    [self.view setImage:background];
    

    Donc j'ai le classique:
    warning: 'UIView' may not respond to '-setImage:'
    J'ai mal fait quelques choses ou bien je ne dois pas en tenir compte vu que moi je le sais que ça répondra bien au runtime? Mais j'aime pas avoir des warning qui traine malgré tout.
  • Philippe49Philippe49 Membre
    mars 2009 modifié #24
    dans 1238247416:

    [self.view setImage:background];
    

    Donc j'ai le classique:
    warning: 'UIView' may not respond to '-setImage:'
    J'ai mal fait quelques choses ou bien je ne dois pas en tenir compte vu que moi je le sais que ça répondra bien au runtime? Mais j'aime pas avoir des warning qui traine malgré tout.


    Le compilateur ne peut pas savoir que self.view est une UIImageView. Il est donc logique qu'il te signale un Warning.
    Solution : [(UImageView*)(self.view) setImage:background] qui ne fait rien de spécial, si ce n'est qu'il te sert à  être en paix avec toi-même et avec le compilateur.

    dans 1238247416:

    Mais j'aime pas avoir des warning qui traine malgré tout.

    Moi pareil, j'essaye de les résoudre, car des erreurs peuvent se cacher derrière.
  • GreensourceGreensource Membre
    20:15 modifié #25
    dans 1238245218:

    dans 1238244582:

    - (void)toMenu<br />{<br />	UIView* theSuperView = [[[self view] superview] retain];<br />	[[self view] removeFromSuperview];<br />	[theSuperView addSubview:[superController view]];<br />	[theSuperView release];<br />}
    


    Ok, simplement le couple (retain de la première ligne / release de la dernière) est inutile pour deux raisons :
    • La fameuse superview est retenu par ailleurs, donc il n'y a aucune raison qu'elle soit désallouée entre temps.
    • L'intérieur du code de ta méthode est réalisée dans le même cycle de RunLoop, ce qui fait q'il n'y a aucune chance qu'un release vienne désallouer la superview.   


    J'ai essayer en enlevant les lignes dont tu me parles mais alors ça ne fonctionnais plus, je tombais sur une page blanche. Avec ce code ci:
    <br />[self.view removeFromSuperview];<br />[self.view addSubview:[mainViewController view]];<br />
    
  • AliGatorAliGator Membre, Modérateur
    20:15 modifié #26
    Heu normal, tu enlèves self.view de la superview dans laquelle elle était (hors si une view n'est pas dans une superview, elle est nulle part et n'existe qu'en mémoire, donc elle ne se dessinera pas, faut bien la placer qqpart dans ta "view hierarchy" pour lui dire où elle se place et où elle doit se dessiner)... Et comme dans la ligne d'après tu ne la remets pas dans une autre view...

    Première ligne tu enlève self.view de ta "view hierarchy", 2e ligne tu rajoutes une subview à  cette self.view, mais comme self.view n'est plus dans la view hierarchy donc ne va s'afficher nulle part, bah sa subview non plus.
    Tu as dû t'emmêler les pinceaux dans les vues que tu veux retirer et ajouter, y'a un endroit où t'as pas dû mettre la bonne view/variable... D'ailleurs tes 2 lignes que tu mets dans ton post ne correspondent PAS aux 4 lignes que tu mets juste avant dans ton "toMenu" auxquelles tu aurais enlevé le retain en première ligne et le release en dernière ligne ^^
  • GreensourceGreensource Membre
    20:15 modifié #27
    Exact, j'ai fait une petit erreur à  recopié mais même si je fait ça:
    <br />// 1- Je retire ma vue de la hiérarchie<br />[self.view removeFromSuperview];<br />// 2- J&#39;ajoute la nouvelle vue qui dois s&#39;afficher<br />[self.view.superView addSubview:[mainViewController view]];
    

    C'est à  la deuxième ligne que je ne comprend pas comment ça peut marcher. Car si j'ai viré self.view de la superView, alors self.view n'a plus de superView? C'était pour ça que j'avais gardé une trace de la superView avec une variable tampon. Tu es sûr Philippe que ça peux marcher sans?
  • AliGatorAliGator Membre, Modérateur
    20:15 modifié #28
    En effet.

    Ca peut marcher sans le retain, parce que la (future-ex) superview de ta self.view... est elle même retenue quelque part, elle a elle aussi une superview dans laquelle elle est pour s'afficher elle-même.
    Par contre en effet quand tu as retiré self.view de sa superview, self.view.superview ne doit plus pointer sur rien.

    Donc oui il faut que tu utilises une variable tampon je pense, mais c'est pas utile de lui envoyer un retain pour autant.
    D'autant plus que ce sont les superviews qui retiennent leurs subview, et pas les views qui retiennent leurs superviews. Sur le principe de la règle expliquée plus génériquement ici dans la doc Apple d'ailleurs : quand un objet est "contenu" dans un autre objet (ici une view qui contient des subview dans ton cas), la pratique veux que le parent retienne ses enfants, mais les enfants n'ont qu'un pointeur vers leur parent (ici tes subview qui ont un pointeur vers leur superview), mais pas "retenu" : la superview n'est donc pas retenue (retained) par ses enfants, mais l'inverse.

    Du coup le fait que tu fasses removeFromSuperView va enlever la subview de sa superview, le retainCount de ta subview va décrémenter (puisqu'elle était retenue par sa superview avant), par contre le retainCount de ta superview, lui, ne va pas décrémenter quand tu lui enlèves une subview, il ne a pas bouger. Donc pas de risque que ta superview soit détruite car plus retenue rien que par le fait que tu lui enlèves ta subview.

    Donc variable tampon, oui, retain+release sur la superview que tu stockes en variable tampon, pas utile.
    - (void)toMenu<br />{<br />	UIView* theSuperView = self.view.superview;<br />	[[self view] removeFromSuperview];<br />	[theSuperView addSubview:[superController view]];<br />}
    
  • Philippe49Philippe49 Membre
    avril 2009 modifié #29
    Ce n'est pas la référence à  la superview que j'ai dit inutile, c'est le retain que tu as fait dessus, et le release qui s'en suit.

    Voilà  comment on peut écrire :
    - (void)toMenu
    {
    UIView* theSuperView = self view] superview];<br /> [[self view] removeFromSuperview];<br /> [theSuperView addSubview:[superController view;
    }

  • GreensourceGreensource Membre
    mars 2009 modifié #30
    Okayyyy, c'est ce que disais Philippe mais j'avais pas compris ça sur le moment! Très bien, c'est limpide maintenant, merci tout les deux

    [edit] Ah oui juste pour être sur d'avoir bien compris, quand tu dis:
    la pratique veux que le parent retienne ses enfants, mais les enfants n'ont qu'un pointeur vers leur parent (ici tes subview qui ont un pointeur vers leur superview), mais pas "retenu"

    "retenue" == NSObject id =[monObjet retain];
    "un pointeur" == NSObject id = monObjet;
    C'est ça ou pas?
  • AliGatorAliGator Membre, Modérateur
    mars 2009 modifié #31
    Oui tout à  fait c'est ça ;)
    Ou encore si tu utilises les @property, assez courantes dans le iPhone SDK et Objective-C 2.0, quand tu déclares tes @property(retain) (= retenue) ou @property(assign) (= juste un pointeur ou une assignation).

    Quand je disais "retenue" c'était bien dans le sens "à  qui on envoie un retain" en effet.


    Petite note au passage, sinon, bravo pour ton évolution, tu as l'air de bien avoir intégré les concepts de MVC, pour ceux qui se mettent au développement Objective-C/Cocoa/iPhone sans en avoir fait auparavant, rares sont ceux qui captent les concepts aussi rapidement : c'est un très bon exercice que tu as fait là  de faire en sorte que ton appli soit MVC, et les réflexions autour de ça étaient constructives :)
Connectez-vous ou Inscrivez-vous pour répondre.