【问题标题】:Is there a "right" way to have NSTextFieldCell draw vertically centered text?有没有一种“正确”的方式让 NSTextFieldCell 绘制垂直居中的文本?
【发布时间】:2010-11-17 03:20:25
【问题描述】:

我有一个NSTableView 有几个文本列。默认情况下,这些列的dataCell 是Apple 的NSTextFieldCell 类的一个实例,它做了各种奇妙的事情,但它绘制的文本与单元格顶部对齐,我希望文本垂直居中细胞。

NSTextFieldCell 中有一个内部标志,可用于将文本垂直居中,效果很好。但是,由于它是一个内部标志,因此它的使用不受 Apple 的批准,并且它可能会在未来的版本中毫无警告地消失。我目前正在使用这个内部标志,因为它简单有效。 Apple 显然花了一些时间来实现该功能,所以我不喜欢重新实现它的想法。

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

为了记录,这是我目前的“解决方案”:

@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];

【问题讨论】:

  • 我认为 try/catch 块在这种情况下没有任何意义,因为 _cflags 是 C 结构,而不是 Objective C 对象。如果在未来的 Mac OS X 版本中更改了这个结构,可能会发生各种奇怪的事情,但不会抛出异常。
  • @Jakob Egger:你可能是对的。我在互联网上的其他地方找到了该解决方案,并按原样复制了它。
  • 您应该接受 Jakob Egger 的回答。当使用已接受答案中的代码时,在编辑 NSTextFieldCell 时会导致奇怪的故障。雅各布的回答解决了这个问题。
  • 我的应用因使用 _cFlags.vCentered 而被 MAS 拒绝。您已收到警告。
  • @KeithSmiley:感谢提醒!

标签: cocoa customization vertical-alignment nstextfieldcell


【解决方案1】:

没有。正确的做法是将 Field 放在另一个视图中,并使用自动布局或该父视图的布局来定位它。

【讨论】:

  • OK - 但是我将如何使字段调整自身大小(更改其高度)以匹配文本?没有它,我将有一个很好的居中文本字段,但字段内的文本仍将是顶部对齐的,因此文本将出现在父视图的中心上方。
  • 这绝对是正确的答案。允许 NSTextField 依赖其固有大小(根据需要调整字体大小,这将对应于固有大小的变化),然后将其作为子视图添加到容器视图中,该容器视图将相对于包含文本字段的任何内容对其进行框架化这将构成文本字段相对于整个空间的固有大小。
  • @Asher,其实没有,改变字体大小不会导致字段调整大小:dropbox.com/s/9b4vgcp7kuujll2/…
  • 这听起来好像自动布局没有正确配置。
【解决方案2】:

我遇到了同样的问题,这是我所做的解决方案:

1) 在 Interface Builder 中,选择您的 NSTableCellView。确保它与 Size Inspector 中的行高一样大。例如,如果您的行高为 32,则将单元格高度设为 32

2) 确保您的单元格在您的行中正确放置(我的意思是可见)

3) 在 Cell 中选择 TextField 并转到尺寸检查器

4) 您应该会看到“排列”项并选择“在容器中垂直居中”

--> TextField 将在单元格中居中

【讨论】:

    【解决方案3】:

    虽然这是一个很老的问题......

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

    在这种情况下,我建议,

    1. 设置字体。
    2. 调整rowHeight

    也许你会得到安静密集的行。然后,通过设置intercellSpacing 给它们填充。

    例如,

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

    这里你会得到两个属性调整。

    这不适用于多行文本,但如果您不需要多行支持,则非常适合快速垂直居中。

    【讨论】:

    • 这对于单行单元格来说是一个合理的解决方案,它可能涵盖了大多数用例。谢谢!
    【解决方案4】:

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

    -(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 是多行,当用户双击文本进行编辑时,它会回到顶部直到编辑完成。
    • 我的应用只能查看数据,所以我没有遇到这个问题。但是,我相信您必须提供自己的字段编辑器,并做一些棘手的事情来根据用户类型重新调整字段编辑器。请参阅 Apple 文档中的 Working With the Field Editor
    • 在现场书写期间有没有办法重新定位/重绘文本?
    【解决方案5】:

    对于尝试使用 Matt Ball 的 drawInteriorWithFrame:inView: 方法的任何人,如果您已将单元格设置为绘制背景,则这将不再绘制背景。为了解决这个问题,添加一些类似的东西

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

    到您的 drawInteriorWithFrame:inView: 方法的开头。

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

        覆盖 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];
        }
        

        【讨论】:

        • 这似乎是一个很有前途的想法,但它对我不起作用。我创建了NSTextFieldCell 的子类,复制了您的代码,并为我的NSTableView 中的单元格使用了自定义类,但从未调用过titleRectForBounds 方法。
        • 啊,我忘了 NSTextFieldCell 基本上在于它的文档——你是对的,NSTextFieldCell 默认不使用 -titleRectForBounds:。我已经更新了我的答案以覆盖 -drawInteriorWithFrame:inView: 以便在正确的位置绘制文本。
        • 这对 NSTextField 中的 NSTextFieldCell 也有效吗?它似乎不起作用。 编辑 2009?也许它有点老了。 :)
        • @MattBall 如果 NSTextField 中的文本被选中,它会再次回到顶部对齐。
        • @MattBall 在实施您的解决方案后,我得到了一个奇怪的双文本阴影。占位符文本也不是垂直居中吗?有什么想法吗? imgur.com/HaEOl
        猜你喜欢
        • 2011-05-12
        • 2020-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-15
        • 2019-08-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多