Custom NSPopupButton

'soir !


 


Je suis pas trop habitué à  créer des contrôles "custom" en Cocoa. Là , j'ai besoin d'une liste déroulante avec 5 items (de 1 à  5 étoiles). Comme je pourrais être appelé à  utilise ce contrôle plusieurs fois, j'aimerais bien en faire un contrôle "à  moi". Je créé donc une classe qui hérite de NSPopupButton. Dans la méthode awakeFromNib, je créé un menu auquel j'ajoute mes 5 items. Jusque là , tout va bien.


Maintenant, j'aimerais que la largeur de mon contrôle fasse toujours 100 pixels. Je veux que même si dans IB je lui donne une largeur de 200px, il fasse 100px de largeur. J'ai essayé, à  la fin de ma méthode awakeFromNib, de faire un setBoundsSize, mais rien à  faire. C'est toujours la largeur settée dans IB qui fait foi.


 


Une idée ?


 


 


Merci !


 


Réponses

  • salut,


     


    Essaye plutot setFrameSize


  • olofolof Membre

    J'ai essayé (dans le awakeFromNib), mais c'est toujours la largeur mise dans IB qui "gagne"...

  • Une histoire d'autolayout peut-être ?


  • Une histoire d'autolayout peut-être ?


     


    +1 

  • MonsieurPaulMonsieurPaul Membre
    avril 2013 modifié #6

    Si tu le crées avec IB, est-ce que ce n'est pas la fonction -initWithCoder: qui va être appelée ? Dans ce cas là  c'est elle qui devrait être modifiée.


     


    D'une manière générale, pour un contrôle personnalisé, je préfère réécrire  les fonctions - initWithCoder: et - initWithFrame: pour permettre une initialisation depuis IB ou programmatique.


  • olofolof Membre

    Autolayout, mais bien sûr ! Merci jpimbert !


     


     


    Problème résolu. Dans mon contrôle, je charge les contraintes qui y sont associées, je cherche celle qui concerne la largeur et je modifie sa constante à  la largeur voulue.


     


    Par contre, ce système ne fonctionne que si le contrôle a une largeur fixe, non ? Imaginons que la largeur du contrôle soit définie par une contrainte entre le bord du contrôle et le bord de la vue qui le contient. Comment faire dans ce cas ?


     


    Et est-ce que ça signifie qu'un "développeur de contrôle" doit faire en sorte que son contrôle fonctionne autant avec que sans autolayout ?


     


     


    Merci !

  • Dans la methode initWithFrame de ta classe, pas moyen de fixer ce frame aux valeurs voulues?


  • CéroceCéroce Membre, Modérateur

    Imaginons que la largeur du contrôle soit définie par une contrainte entre le bord du contrôle et le bord de la vue qui le contient. Comment faire dans ce cas ?


    Je ne comprends pas bien la question. Ce sont les marges du contrôle qui sont définies par les contraintes, la largeur, elle, est déduite.


     


    Et est-ce que ça signifie qu'un "développeur de contrôle" doit faire en sorte que son contrôle fonctionne autant avec que sans autolayout ?


     


    Il n'y a pas de différence. Que ce soit par auto-resizing ou autolayout, c'est la frame de la vue qui est changée, on n'a pas à  gérer quoi que ce soit de particulier. Ton boulot est de t'assurer qu'elle s'affiche bien à  différentes tailles.

  • Je ne comprends pas bien la question. Ce sont les marges du contrôle qui sont définies par les contraintes, la largeur, elle, est déduite.


    Pas forcément, on peut aussi forcer la taille du contrôle, avec AutoLayout !


    Il n'y a pas de différence. Que ce soit par auto-resizing ou autolayout, c'est la frame de la vue qui est changée, on n'a pas à  gérer quoi que ce soit de particulier. Ton boulot est de t'assurer qu'elle s'affiche bien à  différentes tailles.


    En fait, mon but est que ce contrôle ait toujours la même largeur. C'est peut-être mon but qui n'est pas adéquats avec les guidelines Apple ???

  • AliGatorAliGator Membre, Modérateur

    J'ai pas tout suivi de la conversation, mais surcharger cette méthode ne permettrait pas de faire ce que tu veux (à  savoir forcer une taille fixe de ton contrôle custom) ?


  • Merci Ali, mais ton lien est pour iOS, moi je suis sous OS X !


  • MonsieurPaulMonsieurPaul Membre
    avril 2013 modifié #13

    J'utilise en ce moment un contrôle custom dont la hauteur est fixe. Cela est obtenu en surchargeant la méthode -initWithFrame:


     




    -(id) initWithFrame:(CGRect) frame {
        if (self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 70)]) { ...
     

     


    (hauteur fixe à  70 pts dans ce cas).


     


    Si le contrôle est initialisé depuis IB, c'est la méthode -initWithCoder qu'il faut surcharger. Dans le doute autant surcharger les 2.




  •  


    J'utilise en ce moment un contrôle custom dont la hauteur est fixe. Cela est obtenu en surchargeant la méthode -initWithFrame:


     




    -(id) initWithFrame:(CGRect) frame {
        if (self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 70)]) { ...
     

    (hauteur fixe à  70 pts dans ce cas).


     


    Si le contrôle est initialisé depuis IB, c'est la méthode -initWithCoder qu'il faut surcharger. Dans le doute autant surcharger les 2.



     




    En mode déterrage de vieux sujet...


     


    J'ai essayé, mais ça ne fonctionne pas chez moi. J'ai désactivé AutoLayout sur mon xib, mais rien à  faire. J'ai surchargé -initWithFrame, -initWithCoder et -initWithFrame pullsDown. Dans mon cas, c'est cette dernière méthode qui est appelée. J'ai beau forcer la largeur, je me retrouve avec la largeur fixée dans le xib !


     


    En plus, si je mets un point d'arrêt dans cette méthode -initWithFrame pullsDown, le NSRect reçu en entrée vaut : x=0, y=0, width=0, height=0) !!!

  • berfisberfis Membre
    janvier 2014 modifié #15

    - (void)drawRect:(NSRect)dirtyRect
    {
    NSRect r = self.frame;
    r.size.width = 100;
    [self setFrame:r];
    [super drawRect:r];
    }

    Autre solution :



    - (id)initWithFrame:(NSRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {[self setConstantFrame];}
    return self;
    }

    - (id)initWithCoder:(NSCoder *)aDecoder
    {
    self = [super initWithCoder:aDecoder];
    if (self) {[self setConstantFrame];}
    return self;
    }

    -(void)awakeFromNib {[self setConstantFrame];}

    - (void) setConstantFrame
    {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.0), dispatch_get_main_queue(), ^(void){
    NSRect r = self.frame;
    r.size.width = 100;
    [self setFrame:r];
    });
    }

    Les deux fonctionnent, avec ou sans AutoLayout.


  • AliGatorAliGator Membre, Modérateur
    janvier 2014 modifié #16
    Un drawRect qui modifie la frame : très mauvaise idée.

    Dans drawRect on ne doit que dessiner, surtout pas faire de layout ou de modification de frame ou quoi (surtout que le contexte graphique a été initialisé avant le drawRect pour te permettre de dessiner directement dedans justement... donc si tu changes la taille du viewport de ce contexte, c'est pas top...)



  • - (void)drawRect:(NSRect)dirtyRect
    {
    NSRect r = self.frame;
    r.size.width = 100;
    [self setFrame:r];
    [super drawRect:r];
    }

    Autre solution :



    - (id)initWithFrame:(NSRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {[self setConstantFrame];}
    return self;
    }

    - (id)initWithCoder:(NSCoder *)aDecoder
    {
    self = [super initWithCoder:aDecoder];
    if (self) {[self setConstantFrame];}
    return self;
    }

    -(void)awakeFromNib {[self setConstantFrame];}

    - (void) setConstantFrame
    {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.0), dispatch_get_main_queue(), ^(void){
    NSRect r = self.frame;
    r.size.width = 100;
    [self setFrame:r];
    });
    }

    Les deux fonctionnent, avec ou sans AutoLayout.


     




    Merci berfis, mais :


     


    si j'ajoute la méthode drawRect comme tu le proposes et que j'y mets un breakpoint, il ne s'y arrête jamais. Idem avec ta deuxième proposition. J'ai mis un breakpoint dans chacune des méthodes mais sans succès. J'ai ajouté la méthode initWithFrame: pullsDown en la codant comme initWithFrame et là  ça passe dedans. Mais ça ne change en aucun cas la largeur de mon contrôle. La largeur paramètre dans le xib prend le dessus...

  • berfisberfis Membre
    janvier 2014 modifié #18

    Certes... je me doutais que j'allais m'attirer une remarque :) . Pourtant, mauvaise ou non, l'idée fonctionne, même si dans IB le menu est plus petit que 100 pixels...


     


    Reste la solution 2, mais il faut effectivement différer le changement de taille, sinon ça ne fonctionne pas.


  • berfisberfis Membre
    janvier 2014 modifié #19


     il ne s'y arrête jamais




    ça par contre c'est bizarre. Tu as bien déclaré la classe du pop menu comme étant celle que tu avais dérivée?




  • ça par contre c'est bizarre. Tu as bien déclaré la classe du pop menu comme étant celle que tu avais dérivée?




    Cette fois, c'est tout bon ! Je me suis un peu mélangé les pinceaux en reprenant ce projet qui prenait la poussière depuis pas mal de temps ! Dans mes derniers tests, j'avais en effet mon popup qui n'utilisait pas ma sous-classe...


     


    Merci !

  • Pour avoir régulièrement fait des contrôles customs il ne faut PAS fixer la largeur dans le code.


    Xcode permet dans la majorité des cas de spécifier la largeur d'un contrôle. Pour la hauteur c'est une autre histoire, là  on sous-classe et on s'amuse... NSPopupButton peut être taillé en largeur. Il suffit de dire à  Xcode de ne pas laisser la largeur changer. C'est assez facile, même avec l'autolayout.


  • Moi, j'essaie de dépanner, hein, mais c'est vrai que le problème de départ est curieux:


     



    Je veux que même si dans IB je lui donne une largeur de 200px, il fasse 100px de largeur.



     


    Il y a un petit côté schizoà¯de dans l'énoncé:


    " Mouah ah ah, je vais te le régler à  200 pixels, ton contrôle, rien que pour te pourrir la vie...


    " Même pas peur, dès que je le récupère dans le code, je le remets à  100!


    " Ah oui? Tiens, prends cet AutoLayout dans la tronche!


    " J'avais prévu le coup, rien à  cirer de tes constraints... hiark, hiark...


    " Grrr... I'll be back! "


     


    On vit une époque formidable.


  • Mon contrôle est une liste déroulante qui contient de 1 à  5 étoiles. Une telle liste qui fait 200 pixels de large, c'est juste très moche. Je trouvais plus pratique de fixer la largeur du contrôle plutôt que de devoir documenter quelque part que pour un bon rendu, la largeur doit être de 100 pixels...


    Je trouve que dans ce cas, il s'agit d'un contrôle pour lequel choisir une largeur est juste inutile...


     


    Voilà  !

  • Pourquoi ne pas choisir un NSLevelIndicator en mode Rating, éditable? 


  • En effet, et c'est droit dans la direction où je vais depuis hier soir. Je ne connaissais pas cette possibilité... J'en ai pas parlé dans ce thread parce que je trouve que ça sort un peu du sujet...


  • AliGatorAliGator Membre, Modérateur
    Sinon, les méthodes sizeThatFits: et intrisicSize & co sont faites pour ça. C'est ce qu'utilisent tous les contrôles ayant une taille fixe imposée.
  • CocoaCafé  â†’ Développement OS X SDK - Mac  â†’ OS X: utilisation des classes Cocoa  ;)


  • AliGatorAliGator Membre, Modérateur
    janvier 2014 modifié #28

    CocoaCafé  â†’ Développement OS X SDK - Mac  â†’ OS X: utilisation des classes Cocoa  ;)

    Oui bah c'est pareil : ça s'appelle juste fittingSize sous OSX (au lieu de "sizeThatFits:" sur iOS), et pour l'autre c'est intrisicContentSize sur OSX comme sur iOS (j'avais mis "intrisicSize" de mémoire sur mon poste précédent sans vérifier, car je supposais que vous étiez assez grand pour ouvrir la doc de NSView tout seuls pour aller chercher plus de détails à  partir de mes pointeurs... ;))
Connectez-vous ou Inscrivez-vous pour répondre.