ios android 图文混排,019-谈谈iOS 图文混排

一句话:画path,用Textkit;或者Core Text留位置。

iOS开发中要实现图文混排,简单从API层面看,有两种方法:

(1)用TextKit,设置一个path,然后文字会围绕这个path来布局,值得一提的是,这个path可以不是规则的,可以画成心形,蝴蝶型,只要你画得出,任何path都可以。

(2)用CoreText,给要放图片的位置先用一个文字或者其他特殊字符占位,我们

先来看看官方文档怎么说:

3d751dbac7f9

CoreText文字布局官方解释.png

这几段话主要的意思是:

(1)在运行时,Core Text 中,通过attributedString创建出一个CTFramesetter,一个CTFramesetter会生成一个或者多个CTFrame,每个CTFrame代表这个一个段落。

(2)CTFramesetter会调用CTTypesetter,用来将我们设置好的attribute设置到字符上面,比如对齐,缩进,行间距等等。

(3)每个CTFrame对象中包含了这个段落的所有“行”对象,每个行对象就代表一行的字符,就是上图中的CTLine对象。

(4)每个CTLine包含一个或多个CTRun,每个CTRun代表具有相同attributes(属性)和direction(方向)的字符。

CTRun中有对应的属性信息和frame信息,通过下面两个API可以获取到:

NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);

// 获取run的宽度,并且将上行高度和下行高度保存在对应的&runAscent及 &runDescent中

CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);

另外值得一提的是,可以通过上行高度和下行高度,得到每行的精确高度,可以用来计算label的真实的高度,我能吐槽

boundingRectWithSize:options:context:

有计算误差么?感兴趣的同学可以试试,数量稍大的字符串用它计算试试就知道了。

知道了原理,我们就可以这样想:

先用某个特殊字符(串)占位,获取其CTRun的位置和尺寸,然后在对应的位置和尺寸上面放上图片,不就是在文字中插入图片了么?最简单的图文混排就出来了嘛。

下面先上核心代码:(回头再传demo到git上,太忙了)

我自己定义的中间对象StringModel,用来保存一些中间值,统一传值用。

3d751dbac7f9

StringModel.png

第一步创建CTFrameSetter:

+(void)createCTFrameSInStringModel:(StringModel *)stringModel

{

stringModel.ctFrameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)stringModel.attributedString);

CGSize size = CGSizeMake(stringModel.width, MAXFLOAT);

//获取最佳高度

CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(stringModel.ctFrameSetter, CFRangeMake(0, stringModel.attributedString.length), nil, size, nil);

stringModel.aspactHeight = coreTextSize.height;

NSLog(@"------最佳高度------》%f",stringModel.aspactHeight);

CGMutablePathRef path = CGPathCreateMutable();

CGPathAddRect(path, NULL, CGRectMake(0, 0, stringModel.width, stringModel.aspactHeight));

stringModel.ctFrame = CTFramesetterCreateFrame(stringModel.ctFrameSetter, CFRangeMake(0, 0), path, NULL);

CFRelease(path);

CFRelease(stringModel.ctFrameSetter);

}

第二步找出标志位字符串的位置,并记录下来:

/**

* 取得所有标记的位置和所占尺寸

*

*/

+ (void)calculateFramesOfCTRunsInStringModel:(StringModel *)stringModel

{

CTFrameRef ctFrame = stringModel.ctFrame;

//获取所有的lines

CFArrayRef lines = CTFrameGetLines(ctFrame);

CFIndex count = CFArrayGetCount(lines);

CGPoint lineOrigins[count];

CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), lineOrigins);

CGFloat heightCount = 0;

stringModel.totalNumberOfLines = (NSInteger)count;

for (int i = 0; i < count; i++) {

CTLineRef line = CFArrayGetValueAtIndex(lines, i);

CGFloat lineAscent;

CGFloat lineDescent;

CGFloat lineLeading;

CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);

//获取每个line中所有的runs

CFArrayRef runs = CTLineGetGlyphRuns(line);

CGFloat runHeight = 0;

for (int j = 0; j < CFArrayGetCount(runs); j++) {//找到并计算出所有标志位run的frame

CGFloat runAscent;

CGFloat runDescent;

CGPoint lineOrigin = lineOrigins[i];

CTRunRef run = CFArrayGetValueAtIndex(runs, j);

NSDictionary *attributes = (NSDictionary *)CTRunGetAttributes(run);

CGRect runRect;

runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &runAscent, &runDescent, NULL);

runRect = CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runRect.size.width,runAscent + runDescent);

runHeight = runRect.size.height;

stringModel.perlineHeight = runHeight;

//runAscent + runDescent 就是每行行高

NSString *flagName = [attributes objectForKey:stringModel.flag];

if (flagName) {//如果有值,代表就是标志位run

CGRect rect;

id objW = stringModel.flagAtrribtuesDic[flagWidthKey];

id objH = stringModel.flagAtrribtuesDic[flagHeightKey];

if ([objH isKindOfClass:[NSNumber class]] && [objW isKindOfClass:[NSNumber class]]) {

NSNumber *width = (NSNumber *)objW;

NSNumber *height = (NSNumber *)objH;

rect.size = CGSizeMake(width.floatValue, height.floatValue);

}

else{

rect.size = CGSizeMake(kDefaultWidth, kDefaultHeight);

}

rect.origin.x = runRect.origin.x + lineOrigin.x;

rect.origin.y = stringModel.aspactHeight - lineOrigin.y - rect.size.height; //坐标变换

NSValue *value = [NSValue valueWithCGRect:rect];

[stringModel.flagsFrameArray addObject:value];

}

CFRelease(run);

CFRelease((__bridge CFDictionaryRef)attributes);

}

heightCount += runHeight;//不含行间距

}

stringModel.heightCount = heightCount;

}

// 待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值