如何在翻转NSView中使用Core Text正确绘制文本
在Flipped NSView中正确使用Core Text绘制文本
这个问题本质是坐标系不匹配导致的——Core Text默认使用左下角为原点的坐标系,而你通过重写isFlipped把NSView改成了左上角为原点,两者y轴方向完全相反,直接绘制就会出现翻转;只给字体加翻转矩阵又会因为Core Text的基线计算逻辑,导致文本位置偏移。
下面给你两种靠谱的解决方案,任选其一都能解决问题:
方案一:翻转CGContext坐标系(推荐)
这种方法是先把整个绘图上下文的坐标系转换成Core Text习惯的左下角原点,绘制完成后再恢复上下文状态,这样Core Text的绘制逻辑就和视图坐标系完美匹配了,位置计算也更直观。
示例代码:
- (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; // 保存当前上下文状态,避免影响其他绘制操作 CGContextSaveGState(context); // 关键:将视图的左上角坐标系转换为Core Text的左下角坐标系 CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0); // ------------------- Core Text绘制代码 ------------------- NSString *text = @"Hello Core Text"; CTFontDescriptorRef fontDesc = CTFontDescriptorCreateWithName((CFStringRef)@"Arial", 24.0); CTFontRef font = CTFontCreateWithFontDescriptor(fontDesc, 24.0, NULL); CFRelease(fontDesc); // 创建带字体属性的字符串 NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:text]; [attrStr addAttribute:(id)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange(0, text.length)]; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrStr); // 这里的pos是你想要的左上角坐标,直接用即可 CGPoint pos = CGPointMake(20, 20); CGContextSetTextPosition(context, pos.x, self.bounds.size.height - pos.y); CTLineDraw(line, context); // ------------------- 释放资源 ------------------- CFRelease(line); CFRelease(font); [attrStr release]; // 恢复上下文状态 CGContextRestoreGState(context); }
方案二:单独调整文本的变换与位置
如果你不想翻转整个上下文,可以针对Core Text的绘制单独做变换,同时调整位置来对齐左上角(需要利用文本的排版属性计算偏移)。
示例代码:
- (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; CGContextSaveGState(context); NSString *text = @"Hello Core Text"; CTFontDescriptorRef fontDesc = CTFontDescriptorCreateWithName((CFStringRef)@"Arial", 24.0); CTFontRef font = CTFontCreateWithFontDescriptor(fontDesc, 24.0, NULL); CFRelease(fontDesc); NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:text]; [attrStr addAttribute:(id)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange(0, text.length)]; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrStr); // 获取文本的上行高度(ascent),Core Text从基线开始绘制,我们要把基线向下移ascent才能让文本左上角对齐 CGFloat ascent = CTLineGetTypographicBounds(line, NULL, NULL, NULL); CGPoint originalPos = CGPointMake(20, 20); // 调整位置:y轴加上ascent,然后通过翻转变换修正方向 CGPoint adjustedPos = CGPointMake(originalPos.x, originalPos.y + ascent); // 翻转文本的绘制方向 CGContextScaleCTM(context, 1.0, -1.0); CGContextSetTextPosition(context, adjustedPos.x, -adjustedPos.y); CTLineDraw(line, context); // 释放资源 CFRelease(line); CFRelease(font); [attrStr release]; CGContextRestoreGState(context); }
为什么之前的方法会出问题?
- 直接绘制不做变换:Core Text按左下角原点绘制,而你的视图是左上角原点,y轴反向,所以文本会上下翻转。
- 只给字体加翻转矩阵:字体本身翻转了,但Core Text还是基于原坐标系计算基线位置,文本的上行高度(ascent)会变成向下延伸,导致文本整体偏移,无法对齐预期的左上角位置。
内容的提问来源于stack exchange,提问作者Unsacrificed
相关产品推荐
相关产品推荐

