Есть ли "правильный" способ, чтобы NSTextFieldCell рисовал вертикально центрированный текст? - PullRequest
51 голосов
/ 05 августа 2009

У меня есть NSTableView с несколькими текстовыми столбцами. По умолчанию dataCell для этих столбцов является экземпляром класса NSTextFieldCell от Apple, который делает все виды замечательных вещей, но рисует текст, выровненный по верху ячейки, и я хочу, чтобы текст располагался вертикально по центру клетка.

В NSTextFieldCell имеется внутренний флаг, который можно использовать для вертикального центрирования текста, и он прекрасно работает. Однако, поскольку это внутренний флаг, его использование не санкционировано Apple, и он может просто исчезнуть без предупреждения в будущем выпуске. В настоящее время я использую этот внутренний флаг, потому что он прост и эффективен. Очевидно, что Apple потратила некоторое время на реализацию этой функции, поэтому мне не нравится идея ее повторной реализации.

Итак, у меня такой вопрос: Как правильно реализовать что-то, что ведет себя точно так же, как NStextFieldCell от Apple, но рисует вертикально центрированный текст, а не выровненный по верху? Для справки, вот мое текущее "решение":

@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

Используется следующим образом:

[[myTableColumn dataCell] setVerticalCentering:YES];

Ответы [ 7 ]

35 голосов
/ 03 марта 2012

Другие ответы не работают для нескольких строк. Поэтому сначала я продолжил использовать недокументированное свойство cFlags.vCentered, но это привело к тому, что мое приложение было отклонено из магазина приложений. В итоге я использовал модифицированную версию решения Мэтта Белла, которая работает для нескольких строк, переноса слов и усеченной последней строки:

-(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;
}

(этот код предполагает ARC; добавьте авто-релиз после attrString.mutableCopy, если вы используете ручное управление памятью)

30 голосов
/ 06 августа 2009

Переопределение NSCell -titleRectForBounds: должно сделать это - это метод, отвечающий за указание ячейке, где должен быть нарисован ее текст:

- (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];
}
4 голосов
/ 03 августа 2010

Для тех, кто пытается сделать это, используя метод drawInteriorWithFrame:inView: Мэтта Болла, он больше не будет рисовать фон, если вы установили ячейку для его рисования. Чтобы решить эту проблему, добавьте что-то вроде

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

к началу вашего drawInteriorWithFrame:inView: метода.

4 голосов
/ 17 августа 2009

Между прочим, это работает хорошо, хотя мне не удалось заставить его оставаться центрированным при редактировании ячейки ... У меня иногда есть ячейки с большим количеством текста, и этот код может привести к их смещению, если текст высота больше, чем ячейка, в которой он пытается центрировать ее по вертикали. Вот мой модифицированный метод:

- (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;
}
2 голосов
/ 20 октября 2013

Хотя это довольно старый вопрос ...

Я считаю, что стиль реализации NSTableView по умолчанию предназначен исключительно для однострочного отображения текста с одинаковым размером и шрифтом.

В этом случае я рекомендую

  1. Установить шрифт.
  2. Настройка rowHeight.

Может быть, вы получите тихие плотные ряды. А затем задайте им отступы, установив intercellSpacing.

Например,

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

Вот что вы получите с двумя настройками свойства.

enter image description here

Это не будет работать для многострочного текста, но достаточно хорошо для быстрого вертикального центра, если вам не нужна многострочная поддержка.

1 голос
/ 22 ноября 2014

Нет. Правильный способ - поместить поле в другое представление и использовать его для автоматического размещения или расположения этого родительского представления.

1 голос
/ 10 июля 2014

У меня была такая же проблема, и вот решение, которое я сделал:

1) В Интерфейсном Разработчике выберите свой NSTableCellView. Убедитесь, что он такой же большой, как высота строки в Инспекторе размеров. Например, если ваша высота строки 32, сделайте вашу высоту ячейки 32

2) Убедитесь, что ваша клетка хорошо расположена в вашем ряду (я имею в виду видимый)

3) Выберите TextField внутри ячейки и перейдите к инспектору размера

4) Вы должны увидеть пункт «Упорядочить» и выбрать «Центрировать по вертикали в контейнере»

-> TextField центрируется в ячейке

...