Bonjour,
Voici juste un petit « How to » pour comprendre et jour avec les delegate sur cocoa.
Admettons vous avez une classe « ListingController » qui hérite d’une UIViewController qui gère une liste d’élément et que vous avez une classe « DetailController » qui hérite aussi d’une UIViewController mais qui donne le detail de l’élément cliqué.
Vous voulez que lorsqu’on click sur un bouton posé sur la vue Détail revenir à la liste principal, puis la recharger. Revenir à la vue principal est assez simple il suffit de crée une méthode « IBAction » dans le controller Detail, liée le bouton à cette méthode via Interface builder puis écrire :
1 2 3 | -(IBAction)goHome { [self.view removeFromSuperview]; } |
Et le tour est joué mais bon, la liste n’est pas rechargé pour autant et il n’y a pas eu la levé d’un évènement pour la classe « Liste ». C’est là que les delegate peuvent nous être utile.
Le principe est de définir des méthodes sur la classe de Detail puis les appeller/utiliser lors du click mais c’est la classe Liste qui les implémentera effectivement.
Voici la marche à suivre :
- Dans la classe Detail : Définir une ou plusieurs méthodes dite « delegate » qui devront être implémenter. On appelle ce genre de déclaration un « protocole ».
- Dans la classe Detail : Définir une propriété de classe de type « id » qui utilise le protocole. Cette propriété représente un lien vers la classe « parente » qui implémentera effectivement notre protocole.
- Dans la classe Detail : Appeller lors du click les méthodes « delegate » sur notre protocole.
- Dans la classe Liste : Implémenter les méthodes delegate dans notre classe « parente ».
Etape 1 :
Dans le fichier « DetailController.h », juste après les #import et avant @interface DetailController : UIViewController { } :
1 2 3 4 | @protocol DetailControllerDelegate @required -(void)RetourListe; @end |
Etape 2 :
Toujours dans le fichier « DetailController.h », après « @interface DetailController : UIViewController { » nous définissons une propriété que nous appellerons « delegate » :
1 | id delegate; |
puis après l’accolade définissant l’interface :
1 2 3 | @property (nonatomic,assign) id < DetailControllerDelegate > delegate; @end |
Notre propriété « delegate » est de type « id » mais « implémente » le protocole précédement utilisé.
Etape 3 :
Nous avons définit une propriété dans le fichier « .h », il faut maintenant s’en servir dans notre fichier « DetailController.m » :
1 2 3 4 5 6 7 8 9 | #import "DetailController.h" @implementation DetailController @synthesize delegate; //synthetisé une propriété signifie que le sdk créera les getters/setters pour nous -(IBAction)goHome { NSLog(@"Go home Liste"); [self.delegate RetourListe]; // dispatch l'évènement à la classe parente. [self.view removeFromSuperview]; // supprime la vue courante } |
C’est fini pour notre classe Detail ! Vous remarquerez qu’à aucun moment il n’y a de lien vers la classe « Liste ». Le couplage est donc très faible entre ces classes. Ce principe de couplage faible est assez puissant si on y réfléchi à deux fois vu que nos classes sont, de fait, totalement indépendantes vis à vis de leur « parent » (ou du contexte d’execution pour être plus bien parlant). Bon bref ça c’était le coup de pub raté pour la puissance du modèle objet.
Etape 4
Implémentons les méthodes ! Dans le fichier « ListingController.h » nous déclarons notre interface comme ceci :
1 2 3 | @interface ListingController : UIViewController < DetailControllerDelegate > { // some code } |
Et dans notre fichier « ListingController.m » il suffit d’implémenter la méthode « RetourListe ». Surtout, ne pas la déclarer dans l’interface (le fichier .h) sinon le programme se mélangera les pinceaux ! (Oui, vous utilisez deux méthodes avec le même nom ayant des portés différentes ..)
1 2 3 4 5 | -(void)RetourListe { NSLog("coucou"); // some code } |
Edit : Oups, erreur. On crée la méthode mais à un aucun moment on dit que notre classe Listing va implémenter les méthodes de detail hormis dans la déclaration (fichier.h). Il faut donc qu’à notre objet « DetailController », juste après qu’il soit instanciée, au moment du click par exemple faire un :
1 2 | DetailController*myView = [[DetailControlleralloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]]; myView.delegate = self; |
On définit que c’est la classe Listing (self) va implémenter les méthodes delegate de détail.
Et voilà, normallement ça doit marcher.
Ce « How to » a été écrit à partir d’un projet personnel, je ne pourrais donc pas donnée des sources qui utilisent ce concept. A l’heure où j’écrit ce billet je n’ai pas de Mac à ma disposition.







le 6 avril 2009 à 22h05
Je test demain :p
ce soir c’est zend et son système d’identification ;)
le 7 avril 2009 à 10h24
Hey.
Alors après test, il semble que le self ([self.view removeFromSuperview];) de la fonction goHome fasse réference à la vue du bouton, et pas à la vue globale :p.
du coup, ça supprime le bouton :).
Il existe une méthode genre _parent?
le 7 avril 2009 à 12h40
Tu peux tester [[self superview] removeFromSuperview]; mais techniquement si ta classe Detail hérite bien d’une UIViewController et que la fonction goHome fasse bien reference à cette classe là, self.view fait référence à la vue du controller. Si Apres la vue du controller est liée à la vue du bouton oui c’est normal, pour savoir à quel vue est liée ton controlleur cela se voit via Interface Builder.
Voilou, de toute façon on voit ça ensemble tout à l’heure :)
le 8 avril 2009 à 09h39
Yes ! ça marche avec l’edit !
En plus je crois que j’ai compris.
J’ai un bouqin maintenant, je vais plus te déranger. Et dans 1 mois et demi c’est moi qui ferai des tutos :)
le 8 avril 2009 à 10h43
Je viens de capter comment le navigation controller gère ses vues :)
Tu le savais surement déjà, mais donc :
En gros comme tu le disait, on peut visualiser la navigation par une suite de vues côte à côte, avec une vue root.
Chaque fois qu’on ajoute une vue au navigation controller, elle est placée dans sa pile.
Le navigation controller donne toutes les méthodes pratiques pour gérer cette pile. La plus haute étant celle affichée.
On peut par exemple récupérer la vue courante, retourner à la home, aller à une vue précise, accéder au tableau des vues contrôlées…
Du coup, avec ton exemple, j’ai fait trois fonctions dans mon controller de vue de base :
– goHome : retourne a la home (popToRootViewControllerAnimated)
– nextView : popToViewController:animated en cherchant la vue suivantes dans le tableau des vues du navigation controller
– prevView : pareil mais en cherchant la vue precedente.
Pour la doc, c’est tout là :
http://devworld.apple.com/iphone/library/documentation/UIKit/Reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/cl/UINavigationController
le 8 avril 2009 à 11h40
Yep, c’est trop ça. Vive la doc :)
En fait moi j’avais mon propre système de pile, un peu à l’arrache, qui empilé vraiment les vues les une sur les autres. Donc un removeFromSuperView me suffisait largement.
le 27 août 2009 à 16h26
exactement ce que je cherchais, depuis 1 heure :-)
merci