50 votes

Existe-t-il une manière "correcte" de faire en sorte que NSTextFieldCell dessine du texte centré verticalement ?

J'ai un NSTableView avec plusieurs colonnes de texte. Par défaut, les dataCell pour ces colonnes est une instance de l'application Apple NSTextFieldCell qui fait toutes sortes de choses merveilleuses, mais qui dessine du texte aligné sur le haut de la cellule, alors que je veux que le texte soit centré verticalement dans la cellule.

Il existe un drapeau interne dans NSTextFieldCell qui peut être utilisé pour centrer verticalement le texte, et cela fonctionne à merveille. Cependant, comme il s'agit d'un indicateur interne, son utilisation n'est pas sanctionnée par Apple et il pourrait tout simplement disparaître sans avertissement dans une prochaine version. J'utilise actuellement ce drapeau interne parce qu'il est simple et efficace. Apple a manifestement consacré du temps à la mise en œuvre de cette fonctionnalité, et je n'aime pas l'idée de la réimplémenter.

Donc, ma question est la suivante : Quelle est la bonne façon d'implémenter quelque chose qui se comporte exactement comme le NStextFieldCell d'Apple, mais qui dessine du texte centré verticalement au lieu d'être aligné en haut ?

Pour mémoire, voici ma "solution" actuelle :

@interface NSTextFieldCell (MyCategories)
- (void)setVerticalCentering:(BOOL)centerVertical;
@end

@implementation NSTextFieldCell (MyCategories)
- (void)setVerticalCentering:(BOOL)centerVertical
{
    @try { _cFlags.vCentered = centerVertical ? 1 : 0; }
    @catch(...) { NSLog(@"*** unable to set vertical centering"); }
}
@end

Utilisé comme suit :

[[myTableColumn dataCell] setVerticalCentering:YES];

36voto

Jakob Egger Points 4282

Les autres réponses ne fonctionnaient pas pour les lignes multiples. Par conséquent, j'ai d'abord continué à utiliser la méthode non documentée cFlags.vCentered mais cela a entraîné le rejet de mon application de la boutique d'applications. J'ai fini par utiliser une version modifiée de la solution de Matt Bell qui fonctionne pour les lignes multiples, le retour à la ligne et une dernière ligne tronquée :

-(void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
    NSAttributedString *attrString = self.attributedStringValue;

    /* if your values can be attributed strings, make them white when selected */
    if (self.isHighlighted && self.backgroundStyle==NSBackgroundStyleDark) {
        NSMutableAttributedString *whiteString = attrString.mutableCopy;
        [whiteString addAttribute: NSForegroundColorAttributeName
                            value: [NSColor whiteColor]
                            range: NSMakeRange(0, whiteString.length) ];
        attrString = whiteString;
    }

    [attrString drawWithRect: [self titleRectForBounds:cellFrame] 
                     options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin];
}

- (NSRect)titleRectForBounds:(NSRect)theRect {
    /* get the standard text content rectangle */
    NSRect titleFrame = [super titleRectForBounds:theRect];

    /* find out how big the rendered text will be */
    NSAttributedString *attrString = self.attributedStringValue;
    NSRect textRect = [attrString boundingRectWithSize: titleFrame.size
                                               options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin ];

    /* If the height of the rendered text is less then the available height,
     * we modify the titleRect to center the text vertically */
    if (textRect.size.height < titleFrame.size.height) {
        titleFrame.origin.y = theRect.origin.y + (theRect.size.height - textRect.size.height) / 2.0;
        titleFrame.size.height = textRect.size.height;
    }
    return titleFrame;
}

(Ce code suppose l'ARC ; ajoutez un autorelease après attrString.mutableCopy si vous utilisez une gestion manuelle de la mémoire)

31voto

Matt Ball Points 4929

Remplacement de la méthode NSCell -titleRectForBounds: devrait le faire -- c'est la méthode responsable de dire à la cellule où dessiner son texte :

- (NSRect)titleRectForBounds:(NSRect)theRect {
    NSRect titleFrame = [super titleRectForBounds:theRect];
    NSSize titleSize = [[self attributedStringValue] size];
    titleFrame.origin.y = theRect.origin.y + (theRect.size.height - titleSize.height) / 2.0;
    return titleFrame;
}

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
    NSRect titleRect = [self titleRectForBounds:cellFrame];
    [[self attributedStringValue] drawInRect:titleRect];
}

5voto

Greg Sexton Points 4621

Pour ceux qui tentent de le faire en utilisant la méthode de Matt Ball. drawInteriorWithFrame:inView: cette dernière ne dessinera plus d'arrière-plan si vous avez configuré votre cellule pour qu'elle en dessine un. Pour résoudre ce problème, ajoutez quelque chose du type

[[NSColor lightGrayColor] set];
NSRectFill(cellFrame);

au début de votre drawInteriorWithFrame:inView: méthode.

4voto

Pour information, cela fonctionne bien, bien que je n'aie pas réussi à le faire rester centré lorsque vous modifiez la cellule... J'ai parfois des cellules avec de grandes quantités de texte et ce code peut les désaligner si la hauteur du texte est plus grande que la cellule dans laquelle il essaie de se centrer verticalement. Voici ma méthode modifiée :

- (NSRect)titleRectForBounds:(NSRect)theRect 
 {
    NSRect titleFrame = [super titleRectForBounds:theRect];
    NSSize titleSize = [[self attributedStringValue] size];
     // test to see if the text height is bigger then the cell, if it is,
     // don't try to center it or it will be pushed up out of the cell!
     if ( titleSize.height < theRect.size.height ) {
         titleFrame.origin.y = theRect.origin.y + (theRect.size.height - titleSize.height) / 2.0;
     }
    return titleFrame;
}

2voto

Eonil Points 19404

Bien que ce soit une vieille question...

Je pense que le style par défaut de l'implémentation de NSTableView est strictement destiné à l'affichage d'une seule ligne de texte avec la même taille et la même police.

Dans ce cas, je recommande,

  1. Définir la police.
  2. Ajuster rowHeight .

Vous obtiendrez peut-être des rangs tranquillement denses. Et puis, donnez-leur du rembourrage en mettant intercellSpacing .

Par exemple,

    core_table_view.rowHeight               =   [NSFont systemFontSizeForControlSize:(NSSmallControlSize)] + 4;
    core_table_view.intercellSpacing        =   CGSizeMake(10, 80);

Voici ce que vous obtiendrez avec l'ajustement de deux propriétés.

enter image description here

Cela ne fonctionnera pas pour un texte de plusieurs lignes, mais c'est suffisant pour un centrage vertical rapide si vous n'avez pas besoin d'un support de plusieurs lignes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X