Вопрос по cocoa, vertical-alignment, customization, nstextfieldcell – Есть ли «правильный» способ заставить NSTextFieldCell рисовать вертикально центрированный текст?

50

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

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

Так; мой вопрос такой:What is the right way to implement something that behaves exactly like Apple's NStextFieldCell, but draws vertically centered text instead of top-aligned?

Для справки, вот мое текущее «решение»:

@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];
У меня было отклонено приложение от MAS для использования_cFlags.vCentered, Вы были предупреждены. Keith Smiley
Я не думаю, что блок try / catch в этом случае имеет какой-либо смысл, поскольку _cflags - это структура C, а не объект Objective C. Если эта структура будет изменена в будущей версии Mac OS X, могут произойти всевозможные странные вещи, но не будет выдано никаких исключений. Jakob Egger
@Jakob Egger: Вы, вероятно, правы. Я нашел это решение в Интернете и скопировал его как есть. e.James
@KeithSmiley: спасибо за хедз-ап! e.James
Вы должны принять ответ Якоба Эггера. Когда используется код из принятого ответа, это вызывает странный сбой, когдаNSTextFieldCell отредактировано. Ответ Якоба решает проблему. Jack

Ваш Ответ

7   ответов
1

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

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

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

4) Вы должны увидеть & quot; Arrange & quot; и выберите & quot; Центрировать по вертикали в контейнере & quot;

- & GT; TextField будет центрироваться в ячейке

35

родолжал использовать недокументированные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, если вы используете ручное управление памятью)

Это сработало для меня, и это очень хорошая реализация - спасибо.
Это работает очень хорошо. Принятый ответ вызвал странный сбой при редактировании ячейки.
Кстати, можно ли отцентрировать текст при редактировании? мойNSTextFieldCell имеет длину в несколько строк, и, когда пользователь дважды щелкает текст для его редактирования, он возвращается к началу, пока редактирование не будет завершено.
Есть ли способ перецентрировать / перерисовать текст во время записи в поле?
Мое приложение может только просматривать данные, поэтому эта проблема не возникла у меня. Тем не менее, я считаю, что вам придется предоставить свой собственный редактор полей и сделать некоторые хитрые вещи, чтобы перестроить редактор полей по мере ввода пользователем. УвидетьWorking With the Field Editor в документах Apple.
4

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

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

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

Звучит так, что автопоставка не настроена должным образом.
@ Asher, на самом деле нет, изменение размера шрифта не приводит к изменению размера поля:dropbox.com/s/9b4vgcp7kuujll2/…
Это абсолютно правильный ответ. Разрешить NSTextField полагаться на свой собственный размер (отрегулируйте размер шрифта по своему усмотрению, который будет соответствовать изменению внутреннего размера), затем добавьте его в качестве подпредставления в представление контейнера, которое будет обрамлять его относительно того, что содержит текстовое поле и это обрамляет внутренний размер текстового поля по отношению к общему пространству.
Хорошо, но тогда как мне сделать так, чтобы Поле само меняло размер (изменял его высоту) в соответствии с текстом? Без этого у меня будет хорошо отцентрированное текстовое поле, но текст внутри поля все равно будет выровнен по верхнему краю, поэтому текст будет отображаться над центром в родительском представлении. e.James
30

-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];
}
Красиво сделано. Это работает как шарм! e.James
Это действительно также с NSTextFieldCell внутри NSTextField? Это не похоже на работу.edit 2009? Может быть, он немного староват. :)
Это кажется многообещающей идеей, но она не работает для меня. Я создал подклассNSTextFieldCellскопировал ваш код и использовал пользовательский класс для ячеек в моемNSTableView, ноtitleRectForBounds метод никогда не вызывается. e.James
@MattBall Если текст в NSTextField выделен, он снова выравнивается по верху.
Ах, я забыл, что NSTextFieldCell в основном лежит в его документации - вы правы, по умолчанию NSTextFieldCell не использует -titleRectForBounds :. Я обновил свой ответ для переопределения -drawInteriorWithFrame: inView: чтобы нарисовать текст в нужном месте.
2

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

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

Set font. Adjust rowHeight.

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

Например,

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

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

enter image description here

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

Это разумное решение для однострочных ячеек, которое, вероятно, охватывает большинство случаев использования. Спасибо! e.James
4

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

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

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

Похожие вопросы