首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

iOS NSAttributedString 與 Core Text 富文本繪製完整指南

     編輯:關於C

核心類關係

  • NSAttributedString:不可變屬性字符串,攜帶文本+屬性(顏色、字體、段落等)
  • NSMutableAttributedString:可變版本,支持動態增刪屬性
  • Core Text:底層排版引擎,CTFramesetter→CTFrame→CTFrameDraw
  • CATextLayer:CALayer 子類,直接支持 NSAttributedString,免手動繪製

構建屬性字符串


NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:@"this is test!"];

// 1. 顏色屬性 (kCTForegroundColorAttributeName)
[attrStr addAttribute:(NSString *)kCTForegroundColorAttributeName
                value:(id)[UIColor redColor].CGColor
                range:NSMakeRange(0, 4)];  // "this" 紅色

[attrStr addAttribute:(NSString *)kCTForegroundColorAttributeName
                value:(id)[UIColor yellowColor].CGColor
                range:NSMakeRange(5, 2)];  // "is" 黃色

// 2. 字體屬性 (kCTFontAttributeName) - 必須用 CTFontRef
UIFont *boldFont = [UIFont boldSystemFontOfSize:14];
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)boldFont.fontName, 14, NULL);
[attrStr addAttribute:(NSString *)kCTFontAttributeName
                value:(id)ctFont
                range:NSMakeRange(0, 4)];
CFRelease(ctFont);

// 3. 下劃線 (kCTUnderlineStyleAttributeName)
[attrStr addAttribute:(NSString *)kCTUnderlineStyleAttributeName
                value:@(kCTUnderlineStyleDouble)
                range:NSMakeRange(0, 4)];

方案一:UIView + Core Text 手動繪製


@interface TView : UIView
@property (nonatomic, strong) NSAttributedString *attrString;
@end

@implementation TView
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) self.backgroundColor = [UIColor clearColor];
    return self;
}

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    NSAttributedString *attrStr = self.attrString;
    if (!attrStr) return;

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // 關鍵:翻轉坐標系 (Core Text Y↑, UIKit Y↓)
    CGContextConcatCTM(ctx, CGAffineTransformScale(
        CGAffineTransformMakeTranslation(0, rect.size.height), 1, -1));

    // 1. Framesetter
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
        (__bridge CFAttributedStringRef)attrStr);

    // 2. Path (繪製區域)
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);

    // 3. Frame
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);

    // 4. 繪製
    CTFrameDraw(frame, ctx);

    // 5. 釋放
    CFRelease(frame);
    CFRelease(path);
    CFRelease(framesetter);
}
@end

方案二:CATextLayer 零代碼繪製(推薦簡單場景)


CATextLayer *textLayer = [CATextLayer layer];
textLayer.string = attrString;  // 直接支持 NSAttributedString
textLayer.frame = CGRectMake(0, 100, 200, 200);
textLayer.contentsScale = [UIScreen mainScreen].scale;  // 視網膜屏清晰
[self.view.layer addSublayer:textLayer];

優勢:自動處理坐標系、支持隱式動畫、代碼極少、性能優(GPU 合成)。
劣勢:複雜排版(多欄、文本環繞、自定義截斷)受限。

段落樣式配置(行間距、對齊、換行)


// 1. 行間距
CTParagraphStyleSetting lineSpacing = {
    .spec = kCTParagraphStyleSpecifierLineSpacingAdjustment,
    .value = &(CGFloat){4.0},
    .valueSize = sizeof(CGFloat)
};

// 2. 換行模式
CTLineBreakMode lineBreak = kCTLineBreakByCharWrapping;  // 或 ByWordWrapping
CTParagraphStyleSetting lineBreakSetting = {
    .spec = kCTParagraphStyleSpecifierLineBreakMode,
    .value = &lineBreak,
    .valueSize = sizeof(CTLineBreakMode)
};

// 3. 創建段落樣式
CTParagraphStyleSetting settings[] = { lineSpacing, lineBreakSetting };
CTParagraphStyleRef paraStyle = CTParagraphStyleCreate(settings, 2);

// 4. 應用到屬性字符串
[attrStr addAttribute:(NSString *)kCTParagraphStyleAttributeName
                value:(id)paraStyle
                range:NSMakeRange(0, attrStr.length)];
CFRelease(paraStyle);

計算文本佈局尺寸


// 核心 API:CTFramesetterSuggestFrameSizeWithConstraints
CFSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(
    framesetter,
    CFRangeMake(0, attrStr.length),
    NULL,  // 無額外限制
    CGSizeMake(maxWidth, CGFLOAT_MAX),  // 寬度固定,高度無限
    NULL   // 可選:返回實際使用範圍
);
// suggestedSize.height 即為所需高度

注意:NSString 的 sizeWithFont 無法準確計算多行行間距,必須用 Core Text API。

坐標系翻轉詳解

Core Text 繼承 Quartz 2D,原點左下 Y 向上;UIKit 原點左上 Y 向下。兩種等價翻轉方式:


// 方式 1:連續變換
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1, -1);

// 方式 2:單次矩陣 (推薦)
CGContextConcatCTM(ctx, CGAffineTransformScale(
    CGAffineTransformMakeTranslation(0, rect.size.height), 1, -1));

內存管理檢查清單

  • CTFontCreateWithName → CFRelease
  • CTFramesetterCreate... → CFRelease
  • CTFrameCreate... → CFRelease
  • CGPathCreateMutable → CFRelease
  • CTParagraphStyleCreate → CFRelease
  • AR 下需手動橋接:__bridge/__bridge_retained/__bridge_transfer

工程化建議

場景推薦方案
簡單富文本展示(標籤、聊天氣泡)CATextLayer 或 UILabel (iOS 6+ 支持 attributedText)
複雜排版(雜誌、電子書、代碼編輯器)Core Text 手動佈局 + 緩存 Framesetter
高性能滾動列表預計算佈局 + 異步繪製 + 圖層緩存
編輯器UITextView + NSTextStorage + 自定義 NSLayoutManager

總結

NSAttributedString 是數據模型,Core Text 是排版引擎,CATextLayer/UIView-drawRect 是渲染途徑。掌握屬性鍵值對應、Framesetter→Frame 流程、坐標系翻轉、段落樣式配置、尺寸計算 API,即可實現從簡單標籤到專業排版的各類富文本需求。優先用高層封裝(UILabel/CATextLayer),複雜場景再下沉 Core Text。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved