You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在翻转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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:10:53