1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
| - (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
// 1.获取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
// [a,b,c,d,tx,ty]
NSLog(@"转换前的坐标:%@",NSStringFromCGAffineTransform(CGContextGetCTM(contextRef)));
// 2.转换坐标系
CGContextSetTextMatrix(contextRef, CGAffineTransformIdentity);
CGContextTranslateCTM(contextRef, 0, self.bounds.size.height);
CGContextScaleCTM(contextRef, 1.0, -1.0);
NSLog(@"转换后的坐标:%@",NSStringFromCGAffineTransform(CGContextGetCTM(contextRef)));
// 3.创建绘制区域,可以对path进行个性化裁剪以改变显示区域
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, self.bounds);
// 4.创建需要绘制的文字
NSMutableAttributedString *attributed = [[NSMutableAttributedString alloc] initWithString:@"这是我的第一个coreText demo,我是要给兵来自老白干I型那个饿哦个呢给个I类回滚igkhpwfh 评估后共和国开不开vbdkaphphohghg 的分工额好几个辽宁省更怕hi维护你不看hi好人佛【井柏然把饿哦个"];
[attributed addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20] range:NSMakeRange(0, 5)];
// 两种方式皆可
[attributed addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(3, 10)];
[attributed addAttribute:(id)kCTForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(0, 2)];
// 设置行距等样式
CGFloat lineSpace = 10; // 行距一般取决于这个值
CGFloat lineSpaceMax = 20;
CGFloat lineSpaceMin = 2;
const CFIndex kNumberOfSettings = 3;
// 结构体数组
CTParagraphStyleSetting theSettings[kNumberOfSettings] = {
{kCTParagraphStyleSpecifierLineSpacingAdjustment,sizeof(CGFloat),&lineSpace},
{kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(CGFloat),&lineSpaceMax},
{kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(CGFloat),&lineSpaceMin}
};
CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(theSettings, kNumberOfSettings);
// 单个元素的形式
// CTParagraphStyleSetting theSettings = {kCTParagraphStyleSpecifierLineSpacingAdjustment,sizeof(CGFloat),&lineSpace};
// CTParagraphStyleRef theParagraphRef = CTParagraphStyleCreate(&theSettings, kNumberOfSettings);
// 两种方式皆可
// [attributed addAttribute:(id)kCTParagraphStyleAttributeName value:(__bridge id)theParagraphRef range:NSMakeRange(0, attributed.length)];
// 将设置的行距应用于整段文字
[attributed addAttribute:NSParagraphStyleAttributeName value:(__bridge id)(theParagraphRef) range:NSMakeRange(0, attributed.length)];
CFRelease(theParagraphRef);
// 插入图片部分
//为图片设置CTRunDelegate,delegate决定留给图片的空间大小
NSString *weicaiImageName = @"about";
CTRunDelegateCallbacks imageCallbacks;
imageCallbacks.version = kCTRunDelegateVersion1;
imageCallbacks.dealloc = RunDelegateDeallocCallback;
imageCallbacks.getAscent = RunDelegateGetAscentCallback;
imageCallbacks.getDescent = RunDelegateGetDescentCallback;
imageCallbacks.getWidth = RunDelegateGetWidthCallback;
// ①该方式适用于图片在本地的情况
// 设置CTRun的代理
CTRunDelegateRef runDelegate = CTRunDelegateCreate(&imageCallbacks, (__bridge void *)(weicaiImageName));
NSMutableAttributedString *imageAttributedString = [[NSMutableAttributedString alloc] initWithString:@" "];//空格用于给图片留位置
[imageAttributedString addAttribute:(NSString *)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:NSMakeRange(0, 1)];
CFRelease(runDelegate);
[imageAttributedString addAttribute:@"imageName" value:weicaiImageName range:NSMakeRange(0, 1)];
// 在index处插入图片,可插入多张
[attributed insertAttributedString:imageAttributedString atIndex:5];
// [attributed insertAttributedString:imageAttributedString atIndex:10];
// ②若图片资源在网络上,则需要使用0xFFFC作为占位符
// 图片信息字典
NSString *picURL =@"http://weicai-hearsay-avatar.qiniudn.com/b4f71f05a1b7593e05e91b0175bd7c9e?imageView2/2/w/192/h/277";
NSDictionary *imgInfoDic = @{@"width":@192,@"height":@277}; // 宽高跟具体图片有关
// 设置CTRun的代理
CTRunDelegateRef delegate = CTRunDelegateCreate(&imageCallbacks, (__bridge void *)imgInfoDic);
// 使用0xFFFC作为空白的占位符
unichar objectReplacementChar = 0xFFFC;
NSString *content = [NSString stringWithCharacters:&objectReplacementChar length:1];
NSMutableAttributedString *space = [[NSMutableAttributedString alloc] initWithString:content];
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)space, CFRangeMake(0, 1), kCTRunDelegateAttributeName, delegate);
CFRelease(delegate);
// 将创建的空白AttributedString插入进当前的attrString中,位置可以随便指定,不能越界
[attributed insertAttributedString:space atIndex:10];
// 5.根据NSAttributedString生成CTFramesetterRef
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributed);
CTFrameRef ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributed.length), path, NULL);
// 6.绘制除图片以外的部分
CTFrameDraw(ctFrame, contextRef);
// 处理绘制图片的逻辑
CFArrayRef lines = CTFrameGetLines(ctFrame);
CGPoint lineOrigins[CFArrayGetCount(lines)];
// 把ctFrame里每一行的初始坐标写到数组里
CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), lineOrigins);
// 遍历CTRun找出图片所在的CTRun并进行绘制
for (int i = 0; i < CFArrayGetCount(lines); i++)
{
// 遍历每一行CTLine
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CGFloat lineAscent;
CGFloat lineDescent;
CGFloat lineLeading; // 行距
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
CFArrayRef runs = CTLineGetGlyphRuns(line);
for (int j = 0; j < CFArrayGetCount(runs); j++)
{
// 遍历每一个CTRun
CGFloat runAscent;
CGFloat runDescent;
CGPoint lineOrigin = lineOrigins[i]; // 获取该行的初始坐标
CTRunRef run = CFArrayGetValueAtIndex(runs, j); // 获取当前的CTRun
NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
CGRect runRect;
runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
// 这一段可参考Nimbus的NIAttributedLabel
runRect = CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runRect.size.width, runAscent + runDescent);
NSString *imageName = [attributes objectForKey:@"imageName"];
if ([imageName isKindOfClass:[NSString class]])
{
// 绘制本地图片
UIImage *image = [UIImage imageNamed:imageName];
CGRect imageDrawRect;
imageDrawRect.size = image.size;
NSLog(@"%.2f",lineOrigin.x); // 该值是0,runRect已经计算过起始值
imageDrawRect.origin.x = runRect.origin.x;// + lineOrigin.x;
imageDrawRect.origin.y = lineOrigin.y;
CGContextDrawImage(contextRef, imageDrawRect, image.CGImage);
} else
{
imageName = nil;
CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[attributes objectForKey:(__bridge id)kCTRunDelegateAttributeName];
if (!delegate)
{
continue; // 如果是非图片的CTRun则跳过
}
// 网络图片
UIImage *image;
if (!self.image)
{
// 图片未下载完成,使用占位图片
image = [UIImage imageNamed:weicaiImageName];
// 去下载图片
[self downLoadImageWithURL:[NSURL URLWithString:picURL]];
} else
{
image = self.image;
}
// 绘制网络图片
CGRect imageDrawRect;
imageDrawRect.size = image.size;
NSLog(@"%.2f",lineOrigin.x); // 该值是0,runRect已经计算过起始值
imageDrawRect.origin.x = runRect.origin.x;// + lineOrigin.x;
imageDrawRect.origin.y = lineOrigin.y;
CGContextDrawImage(contextRef, imageDrawRect, image.CGImage);
}
}
}
CFRelease(path);
CFRelease(framesetter);
CFRelease(ctFrame);
}
|