NSTextField et Delegate

skimpyskimpy Membre
01:39 modifié dans API AppKit #1
Bonjour,

J'avais laissé Cocoa pendant une période et je viens de m'y remettre. J'ai donc repris mon livre "Cocoa par la pratique - 2ème édition" et je refais les exercices proposés.
Dans le chapitre 4, l'auteur propose de faire un programme composé :
- de 2 NSTextField
- d'un bouton "Compter les lettres"
Le but du programme et de saisir du texte, d'appuyer sur le bouton et d'afficher : "la chaine xxxx contient x caractères".
Jusque là , pas trop de difficulté (j'ai créé mon interface avec mes outlets/actions et j'ai créé un contrôleur AppController). Et puis je me suis dit qu'il serait intéressant de désactiver le bouton quand le champ de saisi est vide.

Dans un 1er temps, j'avais fait en sorte que AppController soit le delegate pour le champ de saisi. J'ai donc tiré dans IB la connexion delegate et dans le code d'AppController, rajouté dans le awakeFromNib la ligne [texteSaisi setDelegate:self] et puis implémenté les 2 méthodes suivantes (avec juste un NSLog) :
- (void)textDidBeginEditing:(NSNotification *)aNotification;
- (void)textDidChange:(NSNotification *)aNotification;

mais rien n'apparaissait dans la console.
En regardant la documentation de NSTextField, j'ai vu qu'il fallait en fait surcharger NSTextField. J'ai créé une classe SKTextField dans laquelle j'ai implémenté les méthodes.

<br />@interface SKTextField : NSTextField<br />{<br />}<br />- (void)textDidBeginEditing:(NSNotification *)aNotification;<br />- (void)textDidChange:(NSNotification *)aNotification;<br />@end<br /><br />#import &quot;SKTextField.h&quot;<br /><br />@implementation SKTextField<br /><br />- (void)textDidBeginEditing:(NSNotification *)aNotification<br />{<br />	NSLog(@&quot;textDidBeginEditing&quot;);<br />}<br /><br />- (void)textDidChange:(NSNotification *)aNotification<br />{<br />	NSLog(@&quot;textDidChange&quot;);<br />}<br />@end<br />


Les messages apparaissent bien dans la console quand je saisis du texte.
Le problème où je bute est le suivant : comment activer le bouton dans le delegate (celui-ci n'est pas connu à  ce niveau là  [ibBtnCount setEnabled:YES]) ?

Merci.

Réponses

  • BruBru Membre
    01:39 modifié #2
    Tu n'as pas besoin de surcharger ton textField...

    Abonne ton contôleur à  la notification NSControlTextDidChangeNotification.
    Cette notification est émise lorsque le fieldElditor du contrôle (ici le textFieldCell) est modifié.
    Dans la méthode de réception de la notif, il ne te reste plus qu'à  tester si le champ est vide ou non, puis de positionner l'état de ton bouton.

    .
  • AliGatorAliGator Membre, Modérateur
    01:39 modifié #3
    Une autre solution c'est de faire ça directement par binding, je pense que c'est jouable de binder directement le enabled du Bouton au NSTextField, non ?

    Enfin bon dans tous les cas se faire la main sur les notifications ça ne fait pas de mal ;)
  • skimpyskimpy Membre
    01:39 modifié #4
    Merci pour vos réponses. Je vais donc essayer avec la solution que vous me proposez.
    Par contre juste par curiosité, si j'avais voulu conserver la surcharge de mon  textField, quel aurait été le moyen à  utiliser ?
  • BruBru Membre
    février 2007 modifié #5
    dans 1170866287:

    Par contre juste par curiosité, si j'avais voulu conserver la surcharge de mon  textField, quel aurait été le moyen à  utiliser ?


    Plusieurs méthodes :

    1. exposer ton outlet afin que ton textfield puisse l'utiliser. Cette méthode n'est pas à  recommander, car elle lie le code du textfield à  celui de ton appli (ce qui rend plus difficile la réutilisation de ton textfield dans une autre appli).

    2. utiliser le notificationCenter : là , ton textField émet une notification. Il ne te reste plus qu'à  abonner ton contrôleur pour surveiller cette notif.

    3. implanter un mécanisme de delegate : idem, ton textField appelle le delegate afin de lui faire exécuter une méthode précise.
    (Ceci ne marche pas dans le cas d'un textField en mode édition)

    .
  • skimpyskimpy Membre
    01:39 modifié #6
    Ok, voilà  ce que j'ai fait et qui fonctionne :

    <br />- (id)init<br />{<br />	NSNotificationCenter *nc;<br />	nc = [NSNotificationCenter defaultCenter];<br />	[nc addObserver:self selector:@selector(enableCount) name:NSControlTextDidChangeNotification object:texteSaisi];<br /><br />	return self;<br />}<br /><br />- (void)enableCount<br />{<br />	NSString *chaine;	<br />	chaine = [NSString stringWithString:[texteSaisi stringValue]];<br />	<br />	if([chaine length] &gt; 0)<br />	{<br />		[ibBtnCount setEnabled:YES];<br />		[ibBtnClear setEnabled:YES];<br />	}<br />	else<br />	{<br />		[ibBtnCount setEnabled:NO];<br />		[ibBtnClear setEnabled:NO];<br />	}<br />}<br /><br />- (void)dealloc<br />{<br />	NSNotificationCenter *nc;<br />	nc = [NSNotificationCenter defaultCenter];<br />	[nc removeObserver:self];<br />	<br />	[super dealloc];<br />}<br />
    


    Bru, j'aimerais bien essayer ta méthode 3. Est-ce que tu pourrais me guider davantage ? Ce que j'avais fait avant, c'était dire que AppController était le delegate pour mon textField et j'avais implémenté les 2 méthodes que j'avais citées et tiré la connexion dans IB. Le problème c'est que rien ne se passait. En surchargeant NSTextField et en mettant les méthodes du delegate dans ma nouvelle classe, ça fonctionnait (enfin le NSLog) mais à  partir de ma classe SKTextField, impossible d'accéder au bouton.

    Quelle est la bonne méthode à  suivre pour ta 3ème proposition ?

    Merci.
  • BruBru Membre
    février 2007 modifié #7
    dans 1170871642:

    Bru, j'aimerais bien essayer ta méthode 3. Est-ce que tu pourrais me guider davantage ? Ce que j'avais fait avant, c'était dire que AppController était le delegate pour mon textField et j'avais implémenté les 2 méthodes que j'avais citées et tiré la connexion dans IB. Le problème c'est que rien ne se passait. En surchargeant NSTextField et en mettant les méthodes du delegate dans ma nouvelle classe, ça fonctionnait (enfin le NSLog) mais à  partir de ma classe SKTextField, impossible d'accéder au bouton.
    Quelle est la bonne méthode à  suivre pour ta 3ème proposition ?


    <br />@interface SKTextField : NSTextField<br />{<br />}<br />- (void)textDidBeginEditing:(NSNotification *)aNotification;<br />- (void)textDidChange:(NSNotification *)aNotification;<br />@end<br /><br />#import &quot;SKTextField.h&quot;<br /><br />@implementation SKTextField<br /><br />- (void)textDidBeginEditing:(NSNotification *)aNotification<br />{<br />	NSLog(@&quot;textDidBeginEditing&quot;);<br />}<br /><br />- (void)textDidChange:(NSNotification *)aNotification<br />{<br />&nbsp;  id cible;<br /><br />&nbsp;  // récupération de l&#39;objet &quot;target&quot; (objet qui contient la méthode &quot;action&quot;)<br />&nbsp;  cible=[self target];<br /><br />&nbsp;  if (cible!=nil)<br />&nbsp;  {<br />&nbsp; &nbsp; &nbsp; // objet target défini : maintenant on teste si la méthode &quot;enableCount&quot; est implantée dans la classe de l&#39;objet<br />&nbsp; &nbsp; &nbsp; if ([cible respondsToSelector:@selector(enableCount)])<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp;  // méthode implantée : on peut la déclencher !<br />&nbsp; &nbsp; &nbsp; &nbsp;  [cible textDidChange];<br />&nbsp; &nbsp; &nbsp; }<br />&nbsp;  }<br />}<br />
    


    Dans ce cas, ton textField va tenter d'exécuter la méthode enableCount appartenant à  l'objet qui est target de ton bouton : comme cet objet est normalement ton contrôleur, il aura accès à  ton outlet de bouton.

    Note : le target, c'est l'objet que tu connectes à  ton contrôle quand tu tires une ligne dans IB.

    .
  • skimpyskimpy Membre
    01:39 modifié #8
    Merci Bru pour ces précisions ; cependant ma variable cible est toujours = à  null et du coup, rien ne s'exécute. Alors je ne sais pas si j'ai oublié quelque chose ... si tu as le temps, est-ce que tu pourrais voir où j'ai fait une erreur stp ?

    Merci.

    [Fichier joint supprimé par l'administrateur]
  • BruBru Membre
    01:39 modifié #9
    Tu as tout fait bien.

    Mais...
    J'aurai dû le voir dans la doc : la méthode surchargée textDidChange: est appelée par une notification envoyée par le field editor.

    Un textField qui est en mode édition se "transforme" en field editor, c'est donc un autre objet qui n'a plus rien à  voir avec ton texField d'origine. Ceci explique que le target soit nil.

    Donc, oublie la solution delegate.

    Désolé.

    .
  • skimpyskimpy Membre
    01:39 modifié #10
    Merci d'avoir pris le temps de regarder ce petit programme. Je sais et saurai maintenant qu'il faut utiliser les notifications.
Connectez-vous ou Inscrivez-vous pour répondre.