Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

There is an internal flag in NSTextFieldCell that can be used to vertically center the text, and it works beautifully. However, since it is an internal flag, its use is not sanctioned by Apple and it could simply disappear without warning in a future release. I am currently using this internal flag because it is simple and effective. Apple has obviously spent some time implementing the feature, so I dislike the idea of re-implementing it.

So; my question is this: What is the right way to implement something that behaves exactly like Apple's NStextFieldCell, but draws vertically centered text instead of top-aligned?

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]; 
like image 899
e.James Avatar asked Aug 05 '09 19:08

e.James


People also ask

How do you align text vertically?

Center the text vertically between the top and bottom margins. Select the text that you want to center. in the Page Setup group, and then click the Layout tab. In the Vertical alignment box, click Center.


2 Answers

The other answers didn't work for multiple lines. Therefore I initially continued using the undocumented cFlags.vCentered property, but that caused my app 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 wrapping, and a 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; add an autorelease after attrString.mutableCopy if you use manual memory management)

like image 87
Jakob Egger Avatar answered Sep 20 '22 18:09

Jakob Egger


Overriding NSCell's -titleRectForBounds: should do it -- that's 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]; } 
like image 36
Matt Ball Avatar answered Sep 22 '22 18:09

Matt Ball