web-dev-qa-db-fra.com

Existe-t-il une "bonne" méthode pour que NSTextFieldCell dessine un texte centré verticalement?

J'ai une NSTableView avec plusieurs colonnes de texte. Par défaut, la dataCell de ces colonnes est une instance de la classe NSTextFieldCell d’Apple, qui fait toutes sortes de choses merveilleuses, mais elle dessine un texte aligné avec le haut de la cellule et je veux qu’il soit centré verticalement dans la cellule.

Il existe un drapeau interne dans NSTextFieldCell qui peut être utilisé pour centrer le texte verticalement, et cela fonctionne à merveille. Cependant, comme il s'agit d'un drapeau interne, son utilisation n'est pas autorisé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 évidemment passé un certain temps à implémenter cette fonctionnalité, je n’aime donc pas l’idée de la ré-implémenter.

Alors; Ma question est la suivante: Quelle est la bonne façon de mettre en œuvre quelque chose qui se comporte exactement comme le NStextFieldCell d’Apple, mais dessine un texte centré verticalement au lieu d’être aligné au sommet?

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];
49
e.James

Les autres réponses ne fonctionnaient pas pour plusieurs lignes. Par conséquent, j'ai initialement continué à utiliser la propriété undocumented cFlags.vCentered, mais mon application a été rejetée de l'App Store. J'ai fini par utiliser une version modifiée de la solution de Matt Bell qui fonctionne pour plusieurs lignes, le wrapping Word 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 ARC; ajoutez une libération automatique après attrString.mutableCopy si vous utilisez la gestion manuelle de la mémoire)

35
Jakob Egger

Remplacer le -titleRectForBounds: de NSCell devrait le faire - c'est la méthode responsable pour indiquer à 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];
}
30
Matt Ball

Pour votre information, cela fonctionne bien, même si je n’ai pas réussi à le garder centré lorsque vous modifiez la cellule ... j’ai parfois des cellules contenant beaucoup de texte et ce code peut les désaligner si la hauteur du texte est supérieure puis la cellule dans laquelle il essaie de le 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;
}
4
coradine

Pour toute personne utilisant cette méthode à l'aide de la méthode drawInteriorWithFrame:inView: de Matt Ball, celle-ci ne sera plus tracée en arrière-plan si vous avez défini votre cellule pour en dessiner un. Pour résoudre ce problème, ajoutez quelque chose dans le sens de

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

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

4
Greg Sexton

Bien que ce soit une vieille question ...

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

Dans ce cas, je recommande,

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

Vous obtiendrez peut-être des rangées denses et silencieuses. Et ensuite, donnez-leur du padding en définissant intercellSpacing

Par exemple, 

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

Voici ce que vous obtiendrez avec deux ajustements de propriété.

enter image description here

Cela ne fonctionnera pas pour un texte multiligne, mais très bon pour un centre vertical rapide si vous n'avez pas besoin de la prise en charge multiligne.

2
Eonil

J'ai eu le même problème et voici la solution que j'ai faite: 

1) Dans Interface Builder, sélectionnez votre NSTableCellView. Assurez-vous qu'il soit aussi grand que la hauteur de la ligne dans l'inspecteur de taille. Par exemple, si votre hauteur de ligne est 32, définissez votre cellule 32. 

2) Assurez-vous que votre cellule est bien placée dans votre rangée (je veux dire visible)

3) Sélectionnez votre TextField dans votre cellule et accédez à votre inspecteur de taille

4) Vous devriez voir "Arranger" et choisir "Centrer verticalement dans le conteneur"

-> Le TextField va se centrer dans la cellule

1
AllezLesBleus

La bonne façon consiste à placer le champ dans une autre vue et à utiliser la disposition automatique ou la disposition de cette vue parent pour le positionner.

0
Frank Krueger