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

I have an NSTableView with multiple text columns. By default, the dataCell for these columns is an instance of the Apple class NSTextFieldCell , which does all kinds of great things, but it draws text aligned with the top of the cell, and I want the text to be vertically centered in the cell.

NSTextFieldCell has an internal flag that you can use to center the text vertically, and it works great. However, since this is an internal flag, its use is not authorized by Apple, and it may simply disappear without warning in a future version. I am currently using this internal flag because it is simple and efficient. Apple obviously spent some time implementing this feature, so I don't like the idea of ​​reimplementing it.

So my question is this: What is the correct way to implement what behaves exactly the same as Apple NStextFieldCell, but draws vertically centered text instead of aligned to the top?

For the record, here is my current "solution":

 @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 

Used as follows:

 [[myTableColumn dataCell] setVerticalCentering:YES]; 
+48
vertical-alignment cocoa customization
Aug 05 '09 at 19:25
source share
7 answers

Other answers did not work for multiple lines. Therefore, I initially continued to use the undocumented property cFlags.vCentered , but this caused my application to be rejected from the app store. I ended up using a modified version of Matt Bell's solution that works for multiple lines, word wrap, and truncated last line:

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

(This code assumes ARC; adds auto-advertisement after attrString.mutableCopy if you use manual memory management)

+35
Mar 03 2018-12-12T00:
source share

Overriding NSCell -titleRectForBounds: should do this - this is the method responsible for telling the cell where to draw its text:

 - (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]; } 
+29
Aug 6 '09 at 2:17
source share

FYI, this works well, although I was not able to get it to stay centered when you edit the cell ... I sometimes have cells with a lot of text, and this code can cause them to shift if the text is higher than the cell in which she is trying to vertically center her. Here is my modified method:

 - (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
Aug 16 '09 at 23:47
source share

For those trying to use this with the Matt Ball drawInteriorWithFrame:inView: , this will no longer draw the background if you set a cell to draw it. To solve this problem, add something along the lines

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

to the top of your drawInteriorWithFrame:inView: method.

+4
Aug 02 2018-10-10T00:
source share

Although this is a rather old question ...

I believe that the default style for implementing NSTableView is exclusively for single-line text display with all the same size and font.

In this case, I recommend

  • Set the font.
  • Adjust rowHeight .

You may get quiet tight rows. Then add them by installing intercellSpacing .

For example,

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

Here you get two property settings.

enter image description here

This will not work for multi-line text, but is very good for fast vertical center if you do not need multi-line support.

+1
Oct 19 '13 at 21:48
source share

I had the same problem and here is the solution I made:

1) In Interface Builder, select your NSTableCellView. Make sure it measures like the height of the line in the Dimension Inspector. For example, if the row height is 32, make the cell height 32

2) Make sure your cell is well placed in your row (I mean visible)

3) Select your TextField inside your cell and go to your size inspector

4) You should see the "Arrange" item and select "Center vertically in the container"

-> TextField will center itself in the cell

+1
Jul 10 '14 at 15:09
source share

No. The correct way is to put the field in another view and use autoscaling or the layout of the parent view to place it.

0
Nov 22 '14 at 15:59
source share



All Articles