NSFormatter

chaps31chaps31 Membre
17:43 modifié dans API AppKit #1
:brule: 3 heures... 3h... que je m'arrache les cheveux sur cette classe, que je lis la doc succinte d'apple et que... je ne comprends pas grand chose visiblement car rien ne marche... Je veux faire un truc hyper simple et je ne comprends pas qu'avec cocoa cela soit si compliqué mais passons.

J'ai des NSTextField dans mon interface 2 doivent être en majuscule, 2 en nom propre (capitalized) et 1 avec numéro de téléphone (XX.XX.XX.XX.XX).

J'essai majuscule, je crée une sous-classe NSFormatter dans x-code que j'instancie dasn IB et relie avec un NSTextField: formatter connect. OK.

Bon je dois implémenter 3 méthodes obligatoirement :

stringForObjectValue:

getObjectValue:forString:errorDescription:

attributedStringForObjectValue:withDefaultAttributes:


C'est parti :

[tt](NSString *)stringForObjectValue:(id)anObject
{
return [[anObject stringValue] uppercaseString];
}

(BOOL)getObjectValue:forString:errorDescription:
{
//Juste pour vérifier que l'on retourne ce qui faut ?
return YES;
}

(attributedString *)attributedStringForObjectValue:withDefaultAttributes:
{
//Retour d'un attributedString , pour quoi faire dasn mon cas ??
return nil;
}
[/tt]

Je sens bien que je n'ai pas compris grand chose...  :)beta: Et ça bug merveilleusement  :crackboom:- , je ne trouve rien de probant sur le net... Quelques minutes à  m'offrir ?? milles merci... o:)

Réponses

  • AliGatorAliGator Membre, Modérateur
    avril 2008 modifié #2
    Heu déjà  la 2e méthode, c'est une méthode nommée "getXXX" : donc ça retourne des données dans un pointeur passé en paramètre.
    Et là  évidemment il s'agit du paramètre [tt]getObjectValue:(id *)anObject[/tt] : donc il faut que ta méthode retourne l'objet, correspondant à  la chaà®ne passée en 2e paramètre, dans ce paramètre anObject. Et la valeur de retour c'est juste pour indiquer si la conversion a réussi ou si elle est impossible (impossible d'interpréter la chaà®ne pour construire un objet)

    Je ne vois pas ce qui te bloque : il y a même des exemples dans la doc Apple à  ce propos ! getObjectValue:forString:errorDescription:
    Ce qui donne, en l'occurence puisque c'est déjà  une chaà®ne, ben directement la chaà®ne en fait ;)
    - (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error<br />{<br />  *anObject = string; // ou *anObject = [NSString stringWithString: string]; mais je pense pas que ce soit vraiment utile<br />&nbsp; return YES; // je l&#39;avais presque oublié lui... juste pour dire qu&#39;on a pas eu de pb pour convertir la chaà®ne en objet<br />}
    


    Et pour le coup du attributedString, c'est aussi expliqué pourquoi il faut le réimplémenter (c'est pour les cas où tu veux afficher du texte dans un format ou une couleur différente selon l'objet ou sa valeur, genre des nombres négatifs en rouge par ex....) et c'est même expliqué comment il faut la coder :
    Invoke your implementation of stringForObjectValue: to get the non-attributed string, then create an NSAttributedString object with it (see initWithString:). Use the attributes default dictionary to reset the attributes of the string
    donc :
    - (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes<br />{<br />   NSString* str = [self stringForObjectValue:anObject];<br />   return [[[NSAttributedString alloc] initWithString: str attributes:attributes] autorelease];<br />}
    


    Enfin je sais pas j'ai jamais codé de custom NSFormatter mais bon c'est ce que je comprends de la doc en tout cas ;)
  • chaps31chaps31 Membre
    avril 2008 modifié #3
    Je n'ai pas encore testé, mais vraiment merci tu as très bien tout éclairci, je débute et découvre des trucs, par exemple la méthode avec GetXXX qui renvoie l'objet (je n'avais pas compris)...

    Vraiment il y avait un gros bordel et maintenant c'est rangé impec, vraiment merci. o:) Allez je retourne au code j'ai 10 min.

    :adios!: :adios!:
  • chaps31chaps31 Membre
    17:43 modifié #4
    @implementation majuscule

    - (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes
    {
        NSAttributedString* retVal;
      NSString* str = [self stringForObjectValue:anObject];
      [retVal initWithString:str attributes:attributes];
      return retVal;
    }

    - (NSString *)stringForObjectValue:(id)anObject
    {
    return [anObject uppercaseString];
    }

    - (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
    {
    *anObject = string;
    return YES;
    }
    @end

    J'ai corrigé un truc (anobject est un string donc mon stringValue...hum) ça bug toujours à  cause de retVal, le debugger me dit "out of scope", une idée ?

    NB :AttributedString ne répond pas à  alloc d'où la modif.
  • Philippe49Philippe49 Membre
    17:43 modifié #5

    dans 1207938809:

    NB :AttributedString ne répond pas à  alloc d'où la modif.


    Tout NSObject répond à  alloc
  • chaps31chaps31 Membre
    17:43 modifié #6
    D'habitude je ne me pose pas de question, là  j'ai un warning "NSAttributedString does not respond to'-alloc'"... pourquoi d'un coup j'ai ça, - / + ... :why?:
  • Philippe49Philippe49 Membre
    avril 2008 modifié #7
    NSAttributedString *string =[NSAttributedString alloc];

    Il n'y a que la classe qui puisse allouer, puisque l'instance n'existe qu'après allocation.
  • AliGatorAliGator Membre, Modérateur
    avril 2008 modifié #8
    Oui j'ai écrit le code à  la volée un peu rapidement tout à  l'heure, j'avais mis [tt][[retVal alloc] ...][/tt] au lieu de [tt]retVal = [[NSAttributedString alloc] ...][/tt], désolé.

    Du coup il te disait que l'objet "retVal" (de type NSAttributedString bien que pas encore alloué) ne répondait pas à  -alloc (et c'est vrai) : alloc est une méthode de classe comme l'a indiqué Philippe49 (d'où le "+alloc", le "+" mentionnant une méthode de classe), on demande à  la classe NSAttributedString d'allouer une variable de ce type, on demande pas à  la variable retVal qui d'ailleurs n'existe pas encore vraiment. My mistake.

    De plus, j'avais oublié le "return YES" dans le getObjectValue:... (mais tu l'as rajouté tout seul)

    Du coup j'ai édité mon post plus haut pour corriger ces deux étourderies (j'ai carrément tout condensé sur une ligne, direct le return au lieu de la variable retVal intermédiaire), et ça devrait être bon
  • chaps31chaps31 Membre
    17:43 modifié #9
    Z'êtes trop fort..  :P

    Merci à  vous 2 du temps passé à  me répondre (encore et encore), ça fonctionne impec, trop heureux, promis je vais progresser  ;)

    Une remarque étrange j'ai lié mon formatter à  2 champs un nssearchfield et un nstextfield. Pour le premier nickel quand je tape, les lettres sont en majuscules, par contre pour le deuxième la casse est celle de la frappe... Et le formatter est appliqué uniquement quand je déselectionne le nstextfield, là  tout passe en majuscule c'est bizarre non ?
  • Philippe49Philippe49 Membre
    17:43 modifié #10
    Peut-être une lecture de NSObjectController et les bindings , parce qu'il me semble que c'est un problème de binding multiple que tu as là 
  • chaps31chaps31 Membre
    17:43 modifié #11
    Pour l'instant je n'ai aucun Binding (j'apprends sur le tas au fur et à  mesure de ce dont j'ai besoins). En fait le nssearch field est lié à  une IBAction (méthode de recherche) qui se lance à  chaque frappe clavier, donc je pense que cela "valide" le champs à  chaque frappe, ce qui n'est pas le cas pour les nstextfield.

    Je pensais qu'un formatter formattait le champs dès que l'on tapait qque chose pas uniquement après validation...
  • Philippe49Philippe49 Membre
    avril 2008 modifié #12
    dans 1207989992:

    Pour l'instant je n'ai aucun Binding (j'apprends sur le tas au fur et à  mesure de ce dont j'ai besoins).

    Tu as bien raison ...

  • chaps31chaps31 Membre
    17:43 modifié #13
    C'est bien l'utilisateur qui rempli le textField pas le prog, où c'est "sent continuously" ?
  • Philippe49Philippe49 Membre
    avril 2008 modifié #14
    dans 1207994760:

    où c'est "sent continuously" ?


    Dans le panel Attributes --> Control --> State  --> Continuous

  • chaps31chaps31 Membre
    17:43 modifié #15
    Je suis toujours sous tiger, peut être une différence car je n'ai pas de [glow=yellow,2,300]control [/glow]dans mon panel [glow=yellow,2,300]attributes [/glow]de mon nstextfield je n'ai que [glow=yellow,2,300]Send Action On[/glow] qui s'en approche et c'est soit à  la fin de l'édition soit sur enter... pas d'autre choix...

    Ce n'est pas dramatique, mais c'est sûr c'est mieux quand à  chaque frappe le format est pris en compte (depuis j'ai aussi mis en place un format pour les numéro de téléphone).
  • Philippe49Philippe49 Membre
    17:43 modifié #16
    dans 1207998637:

    c'est soit à  la fin de l'édition soit sur enter... pas d'autre choix...


    Prendre ce qui correspond à  l'envoi au fur et à  mesure, et non après Enter
  • chaps31chaps31 Membre
    17:43 modifié #17
    La seule autre possibilité est "end editing", ce qui correspond au changement de champs...
  • AliGatorAliGator Membre, Modérateur
    17:43 modifié #18
    En effet moi qui suis aussi sous Tiger je n'ai que ces deux options là  aussi.
    (Il n'y a que quand j'utilise les bindings que j'ai l'option dans le panel du binding en question "Continuously Updates Value", mais bon)

    Peut-être peux-tu t'en sortir en implémentant en plus les méthodes du NSFormatter dédiées à  l'édition, et non pas que à  l'affichage ?
    You can edit the textual contents of a cell at each keypress and prevent the user from entering invalid characters using isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription: and isPartialStringValid:newEditingString:errorDescription:. You can apply this dynamic editing to things like telephone numbers or social security numbers; the person entering data enters the number only once, since the formatter automatically inserts the separator characters.
  • Philippe49Philippe49 Membre
    17:43 modifié #19
    et [myTextField setContinuous:YES]; ?
  • Philippe49Philippe49 Membre
    avril 2008 modifié #20
    End editing cela veut dire à  la fin de l'édition, donc quand on tape Enter

    Enter Only cela doit être une autre option
  • AliGatorAliGator Membre, Modérateur
    17:43 modifié #21
    dans 1208002958:

    End editing cela veut dire à  la fin de l'édition, donc quand on tape Enter
    Faux, y'a qd on perd le focus sur le TextField aussi
    ;)

    Enter only --> on appuie sur la touche Enter (non, sans blague ?)
    End editing --> on a fini l'édition, soit par appui sur la touche enter, soit par l'appui sur la touche Tab pour passer à  un autre champ, ou par clic autre part (un bouton, le fond de la fenêtre, etc) qui a fait finir l'édition... non ?
  • Philippe49Philippe49 Membre
    avril 2008 modifié #22
    Ouais c'est plus de la nuance, c'est de la finesse ...

    Voilà  ce qu'il y a dans Leopard, il faudrait essayer [myTextField setContinous:YES];

    NSControl 
      setContinuous:
    Sets whether the receiver's cell sends its action message continuously to its target during mouse tracking.
  • chaps31chaps31 Membre
    17:43 modifié #23
    dans 1208002712:

    et [myTextField setContinuous:YES]; ?


    J'avais essayé en mettant ça dans awakefromnib, sans succès.
    Je n'avais pas fait plus attention que ça aux méthodes spéciales numéro de tel... faut que je regarde  ça de plus près d'autant plus qu'avec ma technique j'ai un pb pour les nssearchfield, un qui utilise le formatter majuscule pas de pb, mais un qui utilise le format téléphone, celui-là  une fois relié au formatter je ne peux plus rien taper... (il y a un lien vers un IBAction qui fait travailler ma tableview) Donc je vais regarder les 2 méthodes qui m'ont l'air encore un tantinet hardue à  comprendre, enfin pour moi  :P
  • Philippe49Philippe49 Membre
    avril 2008 modifié #24
    Aligator a raison. C'est la méthode - (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error (ou la seconde du même type) qu'il faut utiliser.

    - (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error
    {
    *newString=[partialString uppercaseString];
    return NO;
    }


    En essayant, cela m'a rappelé qu'on pouvait mettre le CustomFormatter dans le MainMenu.nib, et le connecter dans IB.

  • chaps31chaps31 Membre
    avril 2008 modifié #25
    Merci je vais bidouiller tout ça. Puisque nous sommes là  à  discuter  :P 
    Les nssearchfield, ne sachant pas trop comment ils fonctionnent j'ai créé un IBAction qui fait la recherche puis un scroll sur la tableview relié au nssearchfield ce qui marche impec (en notant que cela ne marche pas avec un nstextfield, la frappe pour ce type de champs ne déclenche pas l'IBAction cela ne semble fonctionner qu'avec les nssearchfield).
    Mais dans les attributs des nssearchfield il y a des options pour les recherches que je n'utilise pas, du coup je me demande si je prend les nssearchfield par le bon avec mes IBAction.

    NB : juste une question c'est quoi cette notation double étoile (NSString **) ?
  • AliGatorAliGator Membre, Modérateur
    17:43 modifié #26
    Pour les Formatter il te reste plus qu'à  en faire un générique, prenant un "type" en paramètre (voire un @selector) pour ne pas t'embêter à  en créer deux très similaires mais en avoir un unique qui peut servir à  la fois pour uppercaseString et capitalizedString ;)
  • Philippe49Philippe49 Membre
    17:43 modifié #27
    dans 1208090004:

    NB : juste une question c'est quoi cette notation double étoile (NSString **) ?


    NSString * avec une étoile = l'adresse où se trouve les données de la NSString

    NSString ** avec deux étoiles = l'adresse de l'adresse où se trouve les données de la NSString

    (Pour un 4 étoiles, c'est plus cher  ...  B) )

    #import <Foundation/Foundation.h>

    void changeMyString(NSString ** stringptr)
    {
    *stringptr=[NSString stringWithString:@n'importe ou];
    }

    int main(int argc, char**argv){
    NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];
    NSString * aString=@n'importe quoi;
    changeMyString(& aString);
    NSLog(aString);
    [pool release];
    return 0;
    }


    Execution

    %gcc pgm.m -o pgm -framework Foundation
    % pgm
    2008-04-13 14:59:06.515 pgm[2493:10b] n'importe ou
    %



  • chaps31chaps31 Membre
    17:43 modifié #28
    Encore merci de vos lumières, je mets ici le code qui marche impec grace à  vous au cas où un débutant comme moi lise ce sujet en recherchant la soolution pour l'implémentation du formatter :

    [tt]#import "majuscule.h"


    @implementation majuscule

    - (NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes
    {

      NSString* str = [self stringForObjectValue:anObject];
      return [[[NSAttributedString alloc] initWithString:str attributes:attributes] autorelease];
    }

    - (NSString *)stringForObjectValue:(id)anObject
    {
    return [NSString stringWithFormat:@%@",anObject];
    }

    - (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error
    {
    *anObject = string;
    return YES;
    }

    - (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error
    {
    *newString=[partialString uppercaseString];
    return NO;
    }

    @end[/tt]

    NB : pour le formatter générique je ne vois pas trop comment en liant à  mes champs dans IB il peut savoir que ce champs c'est un nom propre celui-là  en majuscule...
Connectez-vous ou Inscrivez-vous pour répondre.