Number Formater et Notifications

MulotMulot Membre
23:06 modifié dans API AppKit #1
Voilà , un petit soucis et je ne sais pas trop comment l'aborder :

J'ai un NSTextField qui se voit adjoindre un NSFormater, celui pour rentrer des sommes monaitaires. Jusque là  tout marche impeccablement bien, mais je veux modifier le comportement de ma GUI, et notamment d'un bouton, qui ne doit être activé que si ce textField avec formatter (et un autre TextField "normalé) contiennent des valeurs non nulles.

Je me dis, c'est le moment d'utiliser les notifications, et notamment NSControlTextDidChangeNotification, j'ajoute mon controller au notification center, lui donne une méthode pour updater les boutons en question.

J'implémente la méthode qui sera appelée pour toute notification reçue (elle se charge de vérifier le contenu des deux textfield et de ou non rendre le boutton actif).

Je teste mon application, je peux saisir ce que je veux dans le textfield sans formateur, dans le second en revanche, si je tape "d'une traite", une valeur, il ne va prendre que le premier chiffre, par exemple si je tape 1342, je vais obtenir $ 1,00. Si je suis "au bout" de ma valeur du textfield, et que je fais retour, le valeur ne disparaà®t pas, je suis obligé de positionner le curseur là  ou je veux insérer de nouveaux nombres pour que cela fonctionne.

Autre fait troublant, une fois que j'obtient cette valeur de 1,00 (si j'avais tapé quelque chiffre commencant par 1), je peux incrémenter de 1 en 1 les centimes, en appuyant sur une touche supérieure ou égale à  5 !

Si j'enlève l'objet observateur du centre de notification, le formateur marche très bien, quelle est donc la solution ? Est-ce que la notification intervient sur le comportement du formateur ? Faut dire surcharger une quelconque méthode ?

Réponses

  • fouffouf Membre
    23:06 modifié #2
    Ca serait bien que tu nous donnes la méthode appelée par la notification. Sinon, on dirait que le NSControlTextDidChangeNotification entraine une perte du responder : le textField n'est plus le firstResponder et donc quoi qu'il se passe, tu ne pourras taper qu'un seul caractère à  la fois. Je formule juste une hypothèse, mais sans code, impossible de dire ce qu'il se passe ;)
  • MulotMulot Membre
    23:06 modifié #3
    Merci pour la réponse rapide, voici les deux morceaux de code:

    Dans mon init de mon controller :

    <br /><br />...<br />[[NSNotificationCenter defaultCenter] addObserver:self selector:NSSelectorFromString(@&quot;updateUIButtons&quot;) <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  name:@&quot;NSControlTextDidChangeNotification&quot; <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  object:nil];<br /><br />...<br /><br />
    


    Et la méthode UpdateUIButtons:

    <br />- (void) updateUIButtons {<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; //Using notification to enabled or not buttons<br />&nbsp; &nbsp; BOOL enableOperationButton = ! [ [operationDesignationField stringValue] isEqualToString: @&quot;&quot;] <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  &amp;&amp; ([operationAmountField doubleValue] != 0);<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  <br />&nbsp; &nbsp; [addOperationButton setEnabled: (enableOperationButton)];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; BOOL enableAccountButton = ![ [accountNameField stringValue] isEqualToString: @&quot;&quot;]<br />&nbsp; &nbsp; &nbsp; &nbsp; &amp;&amp; ! [ [accountBankNameField stringValue] isEqualToString: @&quot;&quot;] ;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; [addAccountButton setEnabled: (enableAccountButton)];<br /><br />&nbsp; &nbsp; <br />}<br />
    


    Je pense que le soucis vient du Formater puisque quand je le supprime, aucun soucis, et idem pour deux autres champs (sans formateurs) et un autre bouton.

    J'ai essayé tout les formats possibles pour le formater, rien n'y fait, il ne veut prendre qu'un seul chiffre, et je suis obligé d'utiliser les flèches directionnelles pour insérer éventuellement des chiffres, assez étrange.
  • fouffouf Membre
    23:06 modifié #4
    Je pense que tu devrais essayer avec NSControlTextDidEndEditingNotification. Ca sera plus efficace pour toi (pas besoin de refaire le updateUIButtons a chaque frappe et je pense que tu n'aura plus de pb (tu pourras faire l'édition du textField jusqu'au bout sans interruptions ... )
  • MulotMulot Membre
    23:06 modifié #5
    Oui au départ j'avais opté pour cette solution, et là  je viens de la re-tester, et plus aucun problème au niveau du formatter (je viens de voir qu'il y avait 2 't' ^^).

    Par contre le comportement est plutôt windosien, une fois mes deux textfields remplis, il faut sélectionner ou cliquer sur un autre composant atomique pour que la notification soit émise et que le bouton soit activé. Alors que le comportement de NSControlTextDidChangeNotification me permet encore de saisir du texte une fois le bouton activé, afin de bien montrer à  l'utilisateur que les champs sont requis.

    Il faudrait que je trouve pourquoi le formatter se "bloque" alors que le textfield envoie les notifications. Une petite idée sur la marche à  suivre ? Sous classer NSTextField et ajouter des NSLog dans les méthodes susceptibles d'être appelées voir ou le problème se produit ?
  • BruBru Membre
    23:06 modifié #6
    Le NSNumberFormatter ne se bloque pas, mais et est mal configuré !

    Il existe plusieurs options dont setMaximumFractionDigits: déterminant le nombre de décimales (après virgule) autorisées.
    Je ne sais pas comment tu as créé ce formatter (dans IB ou par prog), mais revoie sa config.

    .
  • MulotMulot Membre
    23:06 modifié #7
    Merci Bru, j'ai juste utilisé celui de IB, tout fait spécialement pour les valeurs monétaires.

    Je vais essayer me tripatouiller avec lui !
  • MulotMulot Membre
    23:06 modifié #8
    Problème problème, j'ai ceci dans ma méthode awakeFromNib de mon controller:

    <br />//Adding a NSNumberFormatter to the operationAmountField<br />&nbsp; &nbsp; NSNumberFormatter *operationAmountFormatter = [[NSNumberFormatter alloc] init];<br />&nbsp; &nbsp; [operationAmountFormatter setMaximumFractionDigits:2];<br />&nbsp; &nbsp; [operationAmountFormatter setAllowsFloats:YES];<br />&nbsp; &nbsp; [operationAmountFormatter setGeneratesDecimalNumbers:YES];<br /><br />&nbsp; &nbsp; [operationAmountField setFormatter:operationAmountFormatter];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; [operationAmountFormatter release];<br /><br />
    


    Lorsque je veux que les notifications soient envoyées à  la fin de l'édition, tout marche comme il faut, le formatter joue son rôle (faudra que j'améliore l'affichage mais bon, détail).

    Par contre, lorsque je teste avec les notifications à  chaque fois que c'est édité, rebelotte, il ne prend en compte que le premier chiffre, même comportement qu'avec celui de IB. Là  y'a quand même un soucis, qui doit venir de la notification, ou du comportement du formatter vis à  vis de la notification non ?

    Que puis-je essayer d'autre pour résoudre se problème, il me fat vraiment ce formatter ainsi que la notification NSControlTextDidChangeNotification, pour que cette partie soit cohérente pour l'utilisateur.

    Merci !
  • BruBru Membre
    23:06 modifié #9
    Scuse, j'avais mal lu ton problème dès le départ.

    En fait, dans ta notif, le fait de lire (par un [operationAmountField doubleValue]) ton textField fait que le formatter attaché entre en action immédiatement (afin que doubleValue te retourne la valeur formatée).
    C'est ce qui provoque cet effet de bord...

    Pour éviter cela, remplace le bloc
    <br />BOOL enableOperationButton = ! [ [operationDesignationField stringValue] isEqualToString: @&quot;&quot;] <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  &amp;&amp; ([operationAmountField doubleValue] != 0);
    


    par
    BOOL enableOperationButton = ! [ [operationDesignationField stringValue] isEqualToString: @&quot;&quot;] <br />&nbsp; &nbsp; &nbsp;  &amp;&amp; (![[[[[operationAmountField window] fieldEditor:NO forObject:nil] textStorage] string]isEqual:@&quot;&quot;]);
    


    Dans ce cas, tu n'interroges pas le textField directement (donc pas de formatage inopiné), mais son fieldEditor (objet qui est responsable de la saisie/affichage de texte) sous-jacent.

    .

  • elfelf Membre
    23:06 modifié #10
    dans 1184800552:

    Je pense que tu devrais essayer avec NSControlTextDidEndEditingNotification. Ca sera plus efficace pour toi (pas besoin de refaire le updateUIButtons a chaque frappe et je pense que tu n'aura plus de pb (tu pourras faire l'édition du textField jusqu'au bout sans interruptions ... )


    Je suis vraiment désolé de poster un message inutile dans ce topic, mais j'ai vraiment besoin de savoir.

    Dans ton avatar, fouf, c'est une fraise ou un carrotte?

    Je ne pouvais pas m'en empècher :P ... bon je sors  :fouf): :(renaud): <3 :o :o
  • MulotMulot Membre
    23:06 modifié #11
    Pas de soucis Bru, très aimable à  toi de m'avoir répondu clairement, je comprend mieux le "problème".

    Si j'essaie d'interpréter le bout de code que m'a donné, en gros je récupère la valeur de mon textField en utilisant les méthodes de super classe afin de ne pas les demander "directement" au TextField, ce qui provoque le soucis du formatter, est-ce que c'est n peu près ça ?

    EDITH: après lecture de la fin de ton post (précipitation !), je me rend compte que tu as pensé à  tout ! :)

    Par contre pour ce qui est du code à  modifier, on utilise des méthodes des super classes, mais je ne comprend pas trop la notion de "window field editor".

    an NSTextField object uses its window's field editor to display and manipulate text. The field editor can be shared by any number of objects, and so its state may be constantly changing. Therefore, it shouldn't be used to display text that demands sophisticated layout (for this you should create a dedicated NSTextView object).


    Ce window field editor contient toutes les données saisies dans des composants appartenant à  la fenêtre du composant sur lequel on a appellé la méthode window ?

    Surtout que dans mon cas j'ai de multiples NSTextField, comment le window field editor peut ne s'occuper que de mon champs ? Parce que aucune référence n'est faite par la suite sur le champs voulu. Moi j'aurais plutôt pensé mettre operationAmountField en paramètre de la méthode " fieldEditor:forObject: ".

    Autre petite question, textStorage n'existe apparemment pas en tant que tel, et j'ai deux possibilités :

    -    [myText textStorageDidProcessEditing:<#(NSNotification *)notification#>

    ou bien

    -    [myText textStorageWillProcessEditing:<#(NSNotification *)notification#>

    Et là  je ne vois pas trop quoi faire, que ça soit pour la gestion de la notification ou laquelle de ces deux méthodes utiliser.

    EDITH BIS: 

    @Elf : "Penser à  ne pas aller manger chez toi *note dans iCal*"
  • BruBru Membre
    23:06 modifié #12
    Stop Mulot, tu n'as pas tout compris...
    Donc je vais te conter la belle histoire des textField, textView et autres joyeusetés dans le monde chocolaté...

    Il était une fois...

    NSTextField est une classe permettant d'afficher un texte à  l'écran.
    Cette classe dérive de NSView, et elle contient un NSTextFieldCell, classe chargée de l'affichage à  l'écran du texte.

    Mais, lorsque NSTextField devient éditable (donc quand le focus est dessus, avec le point d'insertion actif), le NSTextFieldCell "disparaà®t" au profit d'un autre objet, un NSTextView. La classe NSTextView est le pivot de tout ce qui est édition de texte sous cocoa. Enfin, quand l'édition du texte est terminée, NSTextView disparaà®t pour laisser à  nouveau la place au NSTextFieldCell.

    Cependant, pour des raisons de coût (en performance et en empreinte mémoire), il n'existe qu'un seul NSTextView par fenêtre. Il est partagé par tous les NSTextField qui sont éditables.
    On accède à  l'instance de NSTextView de la fenêtre via la méthode fieldEditor:forObject:.

    Le NSTextView communique avec son NSTextField en cours d'édition, donc toutes les notifications de NSTextField sont toujours valables, même si c'est NSTextView qui est en cours d'utilisation.

    Donc, tu peux lire le contenu d'un textField soit par les méthodes stringValue et consorts, soit en accédant au NSTextView quand le textField est en cours d'édition.
    L'avantage de la seconde méthode est de ne pas provoquer la validation du formatter attaché au textField.

    Donc, ton code devrait ressembler à  çà  :

    <br />...<br />[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUIButtons:) <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  name:@&quot;NSControlTextDidChangeNotification&quot; <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  object:nil];<br /><br />...<br />
    


    et

    <br />- (void)updateUIButtons:(NSNotification *)notif<br />{<br />&nbsp;  BOOL enbtn1, enbtn2;<br />&nbsp;  NSTextView *editor;<br /><br />&nbsp;  // retrieving window's field editor<br />&nbsp;  editor=[[[notif object] window] fieldEditor:NO forObject:nil];<br /><br />&nbsp;  // no editor : nothing to do !<br />&nbsp;  if (editor==nil) return;<br /><br />&nbsp;  //Using notification to enabled or not buttons<br /><br />&nbsp;  if ([notif object]==operationDesignationField) enbtn1=([[editor string] length]&gt;0);<br />&nbsp;  else enbtn1=([[operationDesignationField stringValue] length]&gt;0);<br /><br />&nbsp;  if ([notif object]==operationAmountField) enbtn2=([[editor string] length]&gt;0);<br />&nbsp;  else enbtn2=([[operationAmountField stringValue] length]&gt;0);<br /><br />&nbsp;  [addOperationButton setEnabled:(enbtn1&amp;&amp;enbtn2)];<br /><br /><br /><br />&nbsp;  if ([notif object]==accountNameField) enbtn1=([[editor string] length]&gt;0);<br />&nbsp;  else enbtn1=([[accountNameField stringValue] length]&gt;0);<br /><br />&nbsp;  if ([notif object]==accountBankNameField) enbtn2=([[editor string] length]&gt;0);<br />&nbsp;  else enbtn2=([[accountBankNameField stringValue] length]&gt;0);<br /><br />&nbsp;  [addAccountButton setEnabled:(enbtn1&amp;&amp;enbtn2)];<br />}<br />
    


    .
  • MulotMulot Membre
    23:06 modifié #13
    Merci beaucoup Bru pour ton aide, ça marche très bien !

    Par contre une petite question question vis à  vis des notifications, ma méthode updateUIButtons prend maintenant en paramètre une notification, quand j'ai ajouté au controller l'objet observer au Notification Center, j'ai juste précisé @selector(updateUIButtons:)[.b].

    Est que le centre de notification renvoi automatiquement la notification reçue comme paramètre de la méthode à  appeler ?
  • BruBru Membre
    23:06 modifié #14
    dans 1184925041:

    Par contre une petite question question vis à  vis des notifications, ma méthode updateUIButtons prend maintenant en paramètre une notification, quand j'ai ajouté au controller l'objet observer au Notification Center, j'ai juste précisé @selector(updateUIButtons:).
    Est que le centre de notification renvoi automatiquement la notification reçue comme paramètre de la méthode à  appeler ?


    D'après la doc Apple (voir ici), la méthode exécutée par le notificationCenter doit avoir un argument.

    notificationSelector
    Selector that specifies the message the receiver sends notificationObserver to notify it of the notification posting. The method the selector specifies must have one and only one argument.

    L'argument est la notification qui a été postée par l'objet observé.

    Dans ton cas, c'est utile pour récupérer l'id de l'objet (méthode object de NSNotification) qui a émis la notif : en d'autre terme, cela permet de distinguer quel textField est en cours d'édition pour y appliquer la méthode du fieldEditor au lieu de la méthode du stringValue pour savoir si il est vide ou non.

    .
  • MulotMulot Membre
    23:06 modifié #15
    Encore merci à  toi Bru pour ces informations !

    Je commence peut à  peu à  m'imprégner du monde de Cocoa, et à  voir la "philosophie" de celui-ci.
Connectez-vous ou Inscrivez-vous pour répondre.