关于可可:是否有”正确”的方法让NSTextFieldCell绘制垂直居中的文本?

Is there a “right” way to have NSTextFieldCell draw vertically centered text?

我有一个带有多个文本列的NSTableView。默认情况下,这些列的dataCell是Apple NSTextFieldCell类的实例,该类执行各种奇妙的功能,但它绘制的文本与单元格的顶部对齐,并且我希望文本在其中垂直居中单元格。

NSTextFieldCell中有一个内部标志,可用于使文本垂直居中,并且效果很好。但是,由于它是一个内部标志,因此Apple尚未批准其使用,并且在将来的发行版中,它可能会在没有警告的情况下直接消失。我目前正在使用此内部标志,因为它既简单又有效。苹果显然已经花了一些时间来实现该功能,所以我不喜欢重新实现它的想法。

所以;我的问题是:实现与Apple的NStextFieldCell完全一样的行为,但绘制垂直居中的文本而不是顶部对齐的正确方法是什么?

记录下来,这是我当前的"解决方案":

1
2
3
4
5
6
7
8
9
10
11
@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

用途如下:

1
[[myTableColumn dataCell] setVerticalCentering:YES];


其他答案不适用于多行。因此,我最初继续使用未记录的cFlags.vCentered属性,但是这导致我的应用程序被应用程序商店拒绝。我最后使用的是Matt Bell解决方案的修改版本,该解决方案可用于多行,自动换行和最后一行被截断的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-(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之后添加自动释放)。


重写NSCell的-titleRectForBounds:应该做到这一点-这是负责告诉单元格在哪里绘制其文本的方法:

1
2
3
4
5
6
7
8
9
10
11
- (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];
}


对于使用Matt Ball的drawInteriorWithFrame:inView:方法尝试此操作的任何人,如果将单元格设置为绘制一个背景,则将不再绘制背景。为了解决这个问题,请在

的行中添加一些内容

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

drawInteriorWithFrame:inView:方法的开头。


仅供参考,这很好用,尽管在编辑单元格时我没有设法使它保持居中...有时我的单元格中包含大量文本,并且如果文本不正确,此代码可能会导致它们未对齐高度大于它试图垂直居中放置的单元格。这是我修改过的方法:

1
2
3
4
5
6
7
8
9
10
11
- (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;
}

n


尽管这是一个很老的问题...

我相信NSTableView实现的默认样式严格用于具有相同大小的单行文本显示


n