NSAttributedString:不可變屬性字符串,攜帶文本+屬性(顏色、字體、段落等)NSMutableAttributedString:可變版本,支持動態增刪屬性Core Text:底層排版引擎,CTFramesetter→CTFrame→CTFrameDrawCATextLayer: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)];
@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 *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 → CFReleaseCTFramesetterCreate... → CFReleaseCTFrameCreate... → CFReleaseCGPathCreateMutable → CFReleaseCTParagraphStyleCreate → CFRelease__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。