• Billet écrit dans : Mobile, iPhone 06.04.2009

    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 :

    1. 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 ».
    2. 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.
    3. Dans la classe Detail : Appeller lors du click les méthodes « delegate » sur notre protocole.
    4. 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.

    Billet écrit dans : Mobile, iPhone 06.04.2009

    Tags: , , , ,

  • 7 Réactions !!

    WP_Modern_Notepad
    • GrM dit:

      Je test demain :p

      ce soir c’est zend et son système d’identification ;)

    • GrM dit:

      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?

    • Guillaume chave dit:

      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 :)

    • GrM dit:

      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 :)

    • GrM dit:

      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

    • Guillaume chave dit:

      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.

    • greg dit:

      exactement ce que je cherchais, depuis 1 heure :-)
      merci

    Réagissez a cet article ?!

    Attention: Les commentaires sont modérés et ne sont pas visibles automatiquement. Ce n'est pas la peine de re-soumettre votre commentaire.

    Add a comment on FriendFeed




CV Guillaume chave