[Résolu] Outlets, actions, binds, delegate... sans utiliser l'Interface builder.

ShomeiShomei Membre
avril 2010 modifié dans API AppKit #1
Bonjour à  tous,

Je suis nouveau sur ce forum. Et j'ai commencé à  utiliser XCode il y a seulement une semaine. Je poserai probablement des question sur des évidences dans les prochains temps. Par avance, pardon.  :o

Pour mon projet, j'ai besoin créer dynamiquement des NSTextFields dans une vue. La majorité des ces TextFields devront représenter des valeurs numériques CGFloat ; il existe une relation mathématique qui relie les valeurs entre elles. Le choix des valeurs a fournir et des valeurs a obtenir est déterminé par l'utilisateur.

J'ai donc besoin de d'etre informé quand l'utilisateur modifie un nombre et valide le résultat en appuyant sur "entree", sur "tab" ou "shift+tab" pendant l'Edition du NSTextField.
Il n'est pas souhaitable que l'utilisateur aie besoin de cliquer sur un bouton quelque part pour mettre a jour le calcul.
J'ai cherché dans la doc, notamment le "Key-Value Coding Programming Guide", sur la gestion des "events", le "Cocoa Bindings Programming Topics", les textfields et les vues.

Je n'ai pas vraiment trouvé d'exemple concret. Et j'avoue ne pas avoir vraiement compris l'histoire des "key paths" et autres.
Comment est-ce que je peux creer un delegate pour mon TextView ? (J'ai fait une tentative en derivant mon delegate de NSController, sans succes) Je n'ai pas vraiment trouve comment obtenir un observer sur le NSTextField pour savoir quand l'edition est terminée...

[TextField setStringValue:@&quot;Test&quot;];<br />	[TextField setBordered:YES];<br />	[TextField setSelectable:YES];<br />	[TextField setEditable:YES];<br /><br />	[TextField setTarget:Txctrl]; // Txctrl = derivé de NSController<br />	[TextField setAction:@selector(Txctrl)];<br />	<br />	[MaVue addSubview:TextField];<br />	[TextField selectText:self];	<br />	[TextField autorelease];<br />


Remarque : La vue "MaVue" accepte de devenir "first responder". Elle gère les évents de manière a être redimentionnable et repositionnable. Cette vue, qui contient les textfields est en fait destinée a etre insérée dans un document texte, un peu comme une image ou un tableau, un graphique... Mais cette fonctionnalité est encore loin d'être implémentée. Je ne sais pas si utiliser une vue dans ce but est une bonne idée...

Par avance, merci pour votre aide et vos conseils.

Réponses

  • CéroceCéroce Membre, Modérateur
    avril 2010 modifié #2
    Bienvenue à  toi,


    Il va d'abord falloir que tu comprennes quelques principes de bases de Cocoa, à  commencer par le MVC.
    Si je n'ai qu'un seul conseil à  te donner: Achète et suis ce livre.
    Tes questions trouveront rapidement une réponse (ex. : c'est le contrôleur, un bête dérivé de NSObject, qui va faire le lien entre tes NSTextFields).

    Ne te lance pas tout de suite dans les bindings, c'est un sujet assez complexe, d'autant plus quand les objets graphiques sont créés à  la volée. On ne sous-classe pas NSController: on utilise NSObjectController, NSArrayController ou NSTreeController.
  • lgriffielgriffie Membre
    16:55 modifié #3
    dans 1271673142:

    Je n'ai pas vraiment trouvé d'exemple concret. Et j'avoue ne pas avoir vraiement compris l'histoire des "key paths" et autres.
    Comment est-ce que je peux creer un delegate pour mon TextView ? (J'ai fait une tentative en derivant mon delegate de NSController, sans succes) Je n'ai pas vraiment trouve comment obtenir un observer sur le NSTextField pour savoir quand l'edition est terminée...


    Pour recevoir un message permettant de connaitre les événements associés à  un textView tu peux utiliser la souscription aux notifications. Pour en savoir plus sur le pattern Notification ou les autres patterns utilisés dans les framework Cocoa, je te conseil de lire ce livre : http://www.amazon.fr/design-patterns-cocoa-Erik-M-Buck/dp/2744024082/ref=sr_1_1?ie=UTF8&s=books&qid=1271690554&sr=1-1-spell

    Pour les notifications liées au TextView voici un lien vers la documentation d'apple : http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UITextView_Class/Reference/UITextView.html#//apple_ref/doc/uid/TP40006898-CH3-DontLinkElementID_2
  • CéroceCéroce Membre, Modérateur
    16:55 modifié #4
    dans 1271690913:

    Pour recevoir un message permettant de connaitre les événements associés à  un textView tu peux utiliser la souscription aux notifications.

    Dans le cas présent, il est certainement plus simple (et plus efficace) d'utiliser le bon vieux MVC: créer le NSTextField et fixer sa cible (target) = contrôleur. Quand le champ sera validé (appui sur Entrée ou sortie du champ), l'action sera invoquée.
  • lgriffielgriffie Membre
    avril 2010 modifié #5
    En ce qui me concerne pour avoir des informations comme :

    willStartEditing
    didStartEditing

    j'utilise le pattern délégation

    Si je souhaite simplement savoir si le contenu du textview est modifié j'utilise le pattern Notification

    Par contre je parle d'un point de vue SDK iPhone/iPod/iPad, je ne sais pas si la classe TexView est différentes pour mac osx (surement d'ailleurs).

    Par contre rien n'empêche de cumuler les patterns : MVC + Delegate + Notification.
  • mpergandmpergand Membre
    16:55 modifié #6
    dans 1271673142:


    <br />	[TextField setTarget:Txctrl]; // Txctrl = derivé de NSController<br />	[TextField setAction:@selector(Txctrl)];<br />
    




    Spécifier une action pour un textField n'est pas très utile, puisqu'il est l'équivalent de la méthode delegate controlTextDidEndEditing.

    Dans ce cas il faut faire:
    [monTextField setDelegate:monController];

    (controller comme composant du pattern Model-View-Controller)

    Ceci fait, il suffit d'implémenter dans ton controller les méthodes delegate que tu désires, comme par ex:
    <br />- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor<br />{<br />&nbsp; return ??;<br />}
    


    A noter que l'objet delegate est enregistré automatiquement pour recevoir toutes les notifications générées par l'objet qui délègue, ici NSTextField :
    <br />- (void)controlTextDidEndEditing:(NSNotification *)aNotification<br />{<br />&nbsp; NSTextField* textField=[aNotification object];<br />&nbsp; // ...<br />}
    


    Delegate Methods
  • balmatbalmat Membre
    16:55 modifié #7
    Ce que tu décris se prête particulièrement bien à  l'utilisation de bindings. Je pense que l'exemple dans cette doc d'apple peut te permettre de réaliser ce que tu veux en t'en inspirant : http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaBindings/Concepts/WhatAreBindings.html#//apple_ref/doc/uid/20002372-CJBEJBHH

    Petite astuce aussi, pour que ton résultat se mette à  jour lorsque l'une de tes valeurs change, pense à  déclarer la méthode suivante dans ta classe du modèle  :
    <br />+ (NSSet *)keyPathsForValuesAffectingLeNomDeLaVariableContenantLeResultat<br />{<br />&nbsp; &nbsp; return [NSSet setWithObjects:@&quot;premierNombre&quot;, @&quot;deuxiemeNombre&quot;, ..., nil];<br />}<br />
    
  • ShomeiShomei Membre
    16:55 modifié #8
    D'abord, un grand merci pour vos réponses ! (Qui d'ailleurs sont toutes très utiles)

    dans 1271674713:

    Si je n'ai qu'un seul conseil à  te donner: Achète et suis ce livre.


    C'est exactement ce que j'ai fait après avoir posté ce message. (mais il s'agit de la troisième édition, d'occasion) J'ai gardé le nez dedans toute la journée d'hier. La partie la plus intéressante pour mon problème est en fait un challenge (exercice non corrigé).


    dans 1271690913:


    Merci beaucoup. J'ai réussi à  trouver fonctions à  implémenter pour les délégates de NSTextField dans la doc Apple (et grace au livre j'ai compris qu'il fallait que ma classe les implémente et que mon controlleur devait etre un délégate du NSTextField). Mais comment est-ce que je peux savoir qu'il faut que je cherche du coté de UITextView ?) Merci aussi pour le ref vers le livre. Ce sera le prochain sur ma liste.

    dans 1271699497:


    Merci pour l'explication et pour le lien. Je vois qu'il pointe directement au bon endroit de la page. Est-ce qu'il y a un moyen de trouver directement les délégates d'une classe ? De mon coté, j'ai du creuser un moment avant de mettre la main dessus...

    dans 1271674713:

    Dans le cas présent, il est certainement plus simple (et plus efficace) d'utiliser le bon vieux MVC: créer le NSTextField et fixer sa cible (target) = contrôleur. Quand le champ sera validé (appui sur Entrée ou sortie du champ), l'action sera invoquée.


    Donc, j'utilise [textfield setTarget:MonControlleur] ?
    Dans ce cas, quelle méthode je dois implémenter ? Je veux dire quelle fonction de mon contrôleur sera appelée ?
    Et de maniere générale, comment est-ce que je peux utiliser la doc pour trouver quelles fonctions vont etre appellees lorsqu'un objet utilise sa cible (target) ?

    dans 1271699497:

    <br />+ (NSSet *)keyPathsForValuesAffectingLeNomDeLaVariableContenantLeResultat<br />{<br />&nbsp; &nbsp; return [NSSet setWithObjects:@&quot;premierNombre&quot;, @&quot;deuxiemeNombre&quot;, ..., nil];<br />}<br />
    



    Merci. J'ai lu quelque part que les "setters" et "getters" pour une propriété etaient automatiquement appeles lorsqu'on utilise les mecanismes de cle / valeur (setVakueForKey etc...)
    Est-ce que c'est la meme chose ici ?

    Merci pour le lien. C'est l'un des premiers fichiers que j'ai lu apres avoir fait l'exercice du CurrencyConverter. Mais je n'avais rien compris. Je vais le relire. (J'ai deja commencé et il semble bien plus clair)
  • lgriffielgriffie Membre
    16:55 modifié #9
    dans 1271843883:

    D'abord, un grand merci pour vos réponses ! (Qui d'ailleurs sont toutes très utiles)

    dans 1271690913:


    Merci beaucoup. J'ai réussi à  trouver fonctions à  implémenter pour les délégates de NSTextField dans la doc Apple (et grace au livre j'ai compris qu'il fallait que ma classe les implémente et que mon controlleur devait etre un délégate du NSTextField). Mais comment est-ce que je peux savoir qu'il faut que je cherche du coté de UITextView ?) Merci aussi pour le ref vers le livre. Ce sera le prochain sur ma liste.


    Pour savoir si une classe implémente le pattern delegate c'est généralement mentionné dans la classe "maà®tre". Par exemple le UITableView propose une classe delegate UITableViewDelegate. De plus, si la classe à  une propriété "delegate" c'est qu'elle propose ce pattern. Les méthodes delegate peuvent être optionnelles (@optional) ou obligatoires (@required), si pour un contrôleur est défini un delegate :

    @interface maClasse : NSObject &lt;UITextViewDelegate&gt; {
    


    Il faut ensuite dans l'implémentation ajouter les méthodes du delegate pour lesquelles l'on souhaite effectuer des actions. Si le delegate propose des méthodes obligatoires qui ne sont pas implémentées, le compilateur va renvoyer des warning indiquant que la classe n'est pas complètement implémentée.

    Dans l'iPhone, les classes d'interfaces commencent toutes avec UI pour User Interface donc pour un textView la classe est UITextView.
  • CéroceCéroce Membre, Modérateur
    avril 2010 modifié #10
    dans 1271843883:

    Mais comment est-ce que je peux savoir qu'il faut que je cherche du coté de UITextView ?)

    UITextView c'est uniquement pour Cocoa Touch (iPhone & co.).


    Est-ce qu'il y a un moyen de trouver directement les délégates d'une classe ?

    Toutes les classes n'ont pas de délégué. Pour celles qui en ont, les méthodes déléguées sont décrites dans la partie "Delegate methods" de la documentation de la classe.


    Donc, j'utilise [textfield setTarget:MonControlleur] ?
    Dans ce cas, quelle méthode je dois implémenter ? Je veux dire quelle fonction de mon contrôleur sera appelée ?
    Et de maniere générale, comment est-ce que je peux utiliser la doc pour trouver quelles fonctions vont etre appellees lorsqu'un objet utilise sa cible (target) ?


    Les NSTextFields héritent de NSControl (les objets dont hérite une classe sont écrits tout en haut de la doc de la classe).
    NSControl possède une méthode -setTarget: pour fixer la cible (le contrôleur).
    Et une méthode -setAction: pour définir le message à  envoyer.
    Elle s'utilise ainsi:

    [tt][monTextField setAction:@selector(leNomDeMethodeQueJeVeux:)];[/tt]

    La méthode d'action aura la forme:
    [tt]- (IBAction) leNomDeMethodeQueJeVeux:(id)sender;[/tt]
    (Il y a un peu de marge de manoe“uvre pour le type de l'argument, mais c'est pour simplifier).

    Tu te retrouves en terrain connu. Utiliser les méthodes déléguées, comme l'indiquait mpergand, est une alternative valide à  l'utilisation du couple action/cible. Par contre, ne te lance pas tout de suite dans les bindings, tu vas t'y casser les dents. Non pas que ce soit inintéressant, mais ça nécessite beaucoup d'expérience, surtout pour le cas présent.



    J'ai lu quelque part que les "setters" et "getters" pour une propriété etaient automatiquement appeles lorsqu'on utilise les mecanismes de cle / valeur (setVakueForKey etc...)

    Oui, le Key-Value Coding fonctionne ainsi. Il ne faut utiliser le KVC que quand on en a réellement besoin, parce que les noms des variables ne peuvent pas être vérifiés à  la compilation. Là , pas besoin.
  • ShomeiShomei Membre
    16:55 modifié #11
    Merci beaucoup pour vos réponses, pour les explications et directions à  creuser, les liens vers la doc....
    J'ai réussi a faire fonctionner le tout. Les échecs m'ont permis de comprendre un peu plus comment fonctionnait l'interface builder.
    Je ne suis pas encore tout a fait certain de comprendre tout ce que j'ai fait, mais ca viendra avec le temps. La prochaine étape est de réussir a sauvegarder l'ensemble des objets et de trouver un moyen de stocker toutes les connexions que l'utilisateur a crées entre eux, (binds, observations) pour recomposer le tout lors d'un prochain chargement des données.

    Je marque donc le sujet comme résolu. Merci encore pour votre aide précieuse.
Connectez-vous ou Inscrivez-vous pour répondre.