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

iOS18中CoreText的CTRunDraw无法绘制下划线?是特性还是Bug?

iOS18中CTRunDraw不显示下划线的原因与解决方案

结论

这是iOS18对CoreText行为的规范修正(属于特性变更范畴),而非系统Bug。

原因分析

CoreText的API设计有明确的职责划分:

  • CTFrameDraw和CTLineDraw负责完整的文本块/文本行渲染,包含字形、文本装饰(下划线、删除线)、颜色等所有属性的处理。
  • CTRunDraw是最低层级的API,仅负责绘制单个字形组(CTRun)的字形本身,原本就不应该处理下划线这类跨run的装饰属性。

iOS17及之前版本中,CTRunDraw意外渲染了下划线,属于不符合设计预期的行为;iOS18修正了这个逻辑,让CTRunDraw回归其本职功能,不再处理文本装饰属性。

解决方案

方案1:改用CTLineDraw或CTFrameDraw(推荐)

直接使用上层API,无需手动处理下划线,代码更简洁且符合CoreText设计规范:

// 替换CTRunDraw循环为CTLineDraw
for (int i = 0; i < CFArrayGetCount(lines); i++) {
    CTLineRef line = CFArrayGetValueAtIndex(lines, i);
    CGContextSetTextPosition(context, lineOrigins[i].x, lineOrigins[i].y);
    CTLineDraw(line, context); // 此API会自动绘制下划线
}

方案2:手动绘制下划线(若必须使用CTRunDraw)

如果需要精细控制每个CTRun的渲染,可手动提取下划线属性并绘制:

// 在CTRunDraw之后添加手动绘制下划线的逻辑
CFArrayRef runs = CTLineGetGlyphRuns(line);
for (int j = 0; j < CFArrayGetCount(runs); j++) {
    CTRunRef run = CFArrayGetValueAtIndex(runs, j);
    CTRunDraw(run, context, CFRangeMake(0, 0));
    
    // 手动绘制下划线
    CFDictionaryRef runAttrs = CTRunGetAttributes(run);
    NSNumber *underlineStyle = (__bridge NSNumber *)CFDictionaryGetValue(runAttrs, (__bridge const void *)NSUnderlineStyleAttributeName);
    if (!underlineStyle || underlineStyle.integerValue == NSUnderlineStyleNone) {
        continue;
    }
    
    UIColor *underlineColor = (__bridge UIColor *)CFDictionaryGetValue(runAttrs, (__bridge const void *)NSUnderlineColorAttributeName) ?: [UIColor blackColor];
    UIFont *font = (__bridge UIFont *)CFDictionaryGetValue(runAttrs, (__bridge const void *)NSFontAttributeName) ?: [UIFont systemFontOfSize:20];
    
    // 获取run的排版边界
    CGFloat ascent, descent, leading;
    CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
    CGPoint runPos = CGContextGetTextPosition(context);
    
    // 计算下划线位置(基于字体的下划线偏移和粗细)
    CGFloat underlineY = runPos.y - descent + font.underlineOffset;
    CGFloat underlineThickness = font.underlineThickness;
    
    // 绘制下划线
    CGContextSaveGState(context);
    CGContextSetStrokeColorWithColor(context, underlineColor.CGColor);
    CGContextSetLineWidth(context, underlineThickness);
    // 处理粗体下划线样式
    if (underlineStyle.integerValue & NSUnderlineStyleThick) {
        CGContextSetLineWidth(context, underlineThickness * 2);
    }
    CGContextMoveToPoint(context, runPos.x, underlineY);
    CGContextAddLineToPoint(context, runPos.x + runWidth, underlineY);
    CGContextStrokePath(context);
    CGContextRestoreGState(context);
    
    // 更新文本位置,确保后续run的绘制位置正确
    CGContextSetTextPosition(context, runPos.x + runWidth, runPos.y);
}

内容的提问来源于stack exchange,提问作者ximmyxiao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:13:16