老谭笔记

在OSX绘制单行文本垂直居中

在iOS开发过程中,对单行文本的垂直居中似乎是一件非常easy的事情,直接用下面这段代码就可以完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface XXView : UIView
@end
@implementation XXView
- (void)drawRect:(CGRect)rect
{
UIFont *font = [UIFont fontWithName:@"Helvetica Neue" size:13];
NSDictionary *attributes = @{NSFontAttributeName:font,NSForegroundColorAttributeName:[UIColor redColor]};
NSAttributedString *title = [[NSAttributedString alloc] initWithString:@"我是垂直居中的"
attributes:attributes];
[title drawAtPoint:CGPointMake(CGRectGetMidX(self.bounds)-title.size.width/2, CGRectGetMidY(self.bounds)-title.size.height/2)];
}
@end

在OSX平台上,我们一般会这样写这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@interface XXView : NSView
@end
@implementation XXView
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSFrameRect(self.bounds);
NSFont *font = [NSFont fontWithName:@"Helvetica Neue" size:13];
NSDictionary *attributes = @{NSFontAttributeName:font,NSForegroundColorAttributeName:[NSColor redColor]};
NSAttributedString *title = [[NSAttributedString alloc] initWithString:@"我位置有点儿偏下" attributes:attributes];
NSSize txtSize = title.size;
NSPoint drawPoint = NSMakePoint(NSMidX(self.bounds)-txtSize.width/2, NSMidY(self.bounds)-txtSize.height/2);
[title drawWithRect:NSMakeRect(drawPoint.x, drawPoint.y, txtSize.width, txtSize.height)
options:NSStringDrawingUsesLineFragmentOrigin];
}
@end

我们看到的效果将会是这样的:
vertical1
这似乎并不符合预期,我尝试计算了NSFont的leading但仍然不能解决这个问题,经过对不同字体的对比,并绘制出NSAttributedString获得的size边框,就能发现NSFont在顶部总是会多出更多的空白区域,并且不仅仅只是字体leading所占用的区域.
知道问题之后,就算是找到了一个”曲线救国”解决办法,将原有的绘制方法改进为以下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSFrameRect(self.bounds);
NSFont *font = [NSFont fontWithName:@"Helvetica Neue" size:13];
NSDictionary *attributes = @{NSFontAttributeName:font,NSForegroundColorAttributeName:[NSColor redColor]};
NSAttributedString *title = [[NSAttributedString alloc] initWithString:@"我是垂直居中的" attributes:attributes];
NSSize txtSize = title.size;
double topGap = txtSize.height - (font.ascender + fabs(font.descender));
NSPoint drawPoint = NSMakePoint(NSMidX(self.bounds)-txtSize.width/2, NSMidY(self.bounds)-txtSize.height/2);
if ([self isFlipped])
drawPoint.y -= topGap/2;
else
drawPoint.y += topGap/2;
[title drawAtPoint:drawPoint];
}

最终可以获得我们预期的效果,并且支持flip模式:
vertical1