大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
在我们iOS项目开发中,我们经常会遇到图文混排的情况,那么什么是图文混排呢?
创新互联自2013年创立以来,先为伊金霍洛等服务建站,伊金霍洛等地企业,进行企业商务咨询服务。为伊金霍洛企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
那么什么是图文混排呢?
在这里我给大家举个例子大家就明白了,例如我们在微博类,社交聊天应用中常常会遇到各种表情,各种链接的解析。问题来了,图文混排的形式有哪些呢?
图文混排的形式:
1.富文本(attributeString)
我们可以采用attributeString来进行图文混排.例如一个文字上插入一个图片
什么是coreText?
iOS/OSX中用于描述富文本的类是NSAttributedString,顾名思义,它比NSString多了Attribute的概念。它可以包含很多属性,粗体,斜体,下划线,颜色,背景色等等,每个属性都有其对应的字符区域。在OSX上我们只需解析完毕相应的数据,准备好NSAttributedString即可,底层的绘制完全可以交给相应的控件完成。但是在iOS上就没有这么方便,想要绘制Attributed String就需要用到CoreText了。(当然iOS6之后已经有AttributedLabel了。)
使用CoreText进行NSAttributedString的绘制,最重要的两个概念就是CTFrameSetter和CTFrame。
其中CTFramesetter是由CFAttributedString(NSAttributedString)初始化而来,可以认为它是CTFrame的一个Factory,通过传入CGPath生成相应的CTFrame并使用它进行渲染:直接以CTFrame为参数使用CTFrameDraw绘制或者从CTFrame中获取CTLine进行微调后使用CTLineDraw进行绘制。
一个CTFrame是由一行一行的CLine组成,每个CTLine又会包含若干个CTRun(既字形绘制的最小单元),通过相应的方法可以获取到不同位置的CTRun和CTLine,以实现对不同位置touch事件的响应。
ios7 开始,功能强大,简单易用,也可以进行图文混排. TextKit并没有新增的类,他是在原有的文本显示控件上的封装,可以使用平时我们最喜欢使用的UILabel,UITextField,UITextView里面就可以使用了。现在来详细介绍一下.
1).NSAtrributedString
这是所有TextKit的载体,所有的信息都会输入到NSAttributedString里面,然后将这个String输入到Text控件里面就可以显示了。
2).NSTextAttachment
iOS7新增的类,作为文本的附件,可以放文件,可以放数据,以 NSAttachmentAttributeName这个key放入NSAttributedString里面,在表情混排这里,我们将放入image。
3).重载NSTextAttachment
本来是可以直接使用NSTextAttachment,但是我们需要根据文字大小来改变表情图片的大小,于是我们需要重载NSTextAttachment,NSTextAttachment实现了NSTextAttachmentContainer,可以给我们改变返回的图像,图像的大小。
利用UIWebView加载HTML实现图文混排
但是注意:UIWebView本身有内存问题,占用内存相比较而较大不推荐,但是使用比较灵活,
iOS实现图文混排的两个方法
如果你想自定义文本的布局,例如像QQ、微信这样的应用中使用表情,那你多半会用到CoreText,CoreText是iOS、OSX平台的文本处理低层的框架, 可以实现任意的文字编排,更多详细信息请戳官方文档,一般来说, 我们们用下面的代码来实现图文混排:
text = [[NSMutableAttributedString alloc] initWithString:@""];
NSAttributedString *txt1 = [[NSAttributedString alloc] initWithString:@"测试"];
[text appendAttributedString:txt1];
[txt1 release];
CTRunDelegateCallbacks callback;
callback.version = kCTRunDelegateVersion1; //必须指定,否则不会生效,没有回调产生。
callback.dealloc = deallocCallback;
callback.getAscent = getAscent;
callback.getDescent = getDescent;
callback.getWidth = getWidth;
NSDictionary *imgAttr = [[NSDictionary dictionaryWithObjectsAndKeys:@100, @"width", nil] retain];
CTRunDelegateRef delegate = CTRunDelegateCreate(callback, imgAttr);
NSDictionary *txtDelegate = [NSDictionary dictionaryWithObjectsAndKeys:(id)delegate, (NSString*)kCTRunDelegateAttributeName, @100, @"width", nil];
NSAttributedString *imgField = [[[NSAttributedString alloc] initWithString:@" " attributes:txtDelegate] autorelease];
[text appendAttributedString:imgField];
[text appendAttributedString:[[[NSAttributedString alloc] initWithString: @"结束"] autorelease]];
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathAddRect(pathRef, NULL, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)text);
ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), pathRef, NULL);
CFArrayRef lines = CTFrameGetLines(ctFrame);
CGPoint origins[CFArrayGetCount(lines)];
CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), origins);
for (int i = 0; i CFArrayGetCount(lines); i++) {
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CFArrayRef runs = CTLineGetGlyphRuns(line);
for (int j = 0; j CFArrayGetCount(runs); j++) {
CTRunRef run = CFArrayGetValueAtIndex(runs, j);
CGPoint lineOrigin = origins[i];
NSDictionary *meta = (NSDictionary*)CTRunGetAttributes(run);
if (meta ([meta valueForKey:@"width"] != nil)) {
imageLocation.y = lineOrigin.y;
CGFloat offset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
imageLocation.x = lineOrigin.x + offset + self.frame.origin.x;
}
}
}
CFRelease(pathRef);
[self setNeedsDisplay];
一直以来,我认为只有这种方法实现。好吧,其实我没有想过有没有其它实现方法的问题。直到有一天看类似效果的代码时惊奇的发现:怎么 没有CTRunDelegate? 于是就仔细想了一下这个问题,创建CTFrame的时候会指定一个path,通常这个path我会使用一个CGRect完事,然后在 有图片的地方使用CTRunDelegate处理一下,但其实完全可以使用CGMutablePath来画出一块不规则的文本路径,比如:这样,就可以在预定的位置画图片了,而不用会CTRunDelegate来特殊处理,这种方式比较适合图片位置固定的应用。
转自madongsheng
可以使用CoreText, 但学习的话,也要花不短的时间。
或者你可以换一种思路, 例如上面的内容,在接口端解析内容返回一个内容数组。文字是一组,一张图是一组。这样在ios端显示时,只需要解析这个数组就可以了。如果是文本用UILabel展示,如果是图片链接,用UIImageView展示。
图文混排
CTFrameRef textFrame // coreText 的 frame
CTLineRef line // coreText 的 line
CTRunRef run // line 中的部分文字
相关方法:
CFArrayRef CTFrameGetLines (CTFrameRef frame ) //获取包含CTLineRef的数组
void CTFrameGetLineOrigins(
CTFrameRef frame,
CFRange range,
CGPoint origins[] ) //获取所有CTLineRef的原点
CFRange CTLineGetStringRange (CTLineRef line ) //获取line中文字在整段文字中的Range
CFArrayRef CTLineGetGlyphRuns (CTLineRef line ) //获取line中包含所有run的数组
CFRange CTRunGetStringRange (CTRunRef run ) //获取run在整段文字中的Range
CFIndex CTLineGetStringIndexForPosition(
CTLineRef line,
CGPoint position ) //获取点击处position文字在整段文字中的index
CGFloat CTLineGetOffsetForStringIndex(
CTLineRef line,
CFIndex charIndex,
CGFloat* secondaryOffset ) //获取整段文字中charIndex位置的字符相对line的原点的x值
主要步骤:
1)计算并存储文字中保含的所有表情文字及其Range
2)替换表情文字为指定宽度的NSAttributedString
CTRunDelegateCallbacks callbacks;
callbacks.version = kCTRunDelegateVersion1;
callbacks.getAscent = ascentCallback;
callbacks.getDescent = descentCallback;
callbacks.getWidth = widthCallback;
callbacks.dealloc = deallocCallback;
CTRunDelegateRef runDelegate = CTRunDelegateCreate(callbacks, NULL);
NSDictionary *attrDictionaryDelegate = [NSDictionary dictionaryWithObjectsAndKeys:
(id)runDelegate, (NSString*)kCTRunDelegateAttributeName,
[UIColor clearColor].CGColor,(NSString*)kCTForegroundColorAttributeName,
nil];
NSAttributedString *faceAttributedString = [[NSAttributedString alloc] initWithString:@"*" attributes:attrDictionaryDelegate];
[weiBoText replaceCharactersInRange:faceRange withAttributedString:faceAttributedString];
[faceAttributedString release];
3) 根据保存的表情文字的Range计算表情图片的Frame
textFrame 通过CTFrameGetLines 获取所有line的数组 lineArray
遍历lineArray中的line通过CTLineGetGlyphRuns获取line中包含run的数组 runArray
遍历runArray中的run 通过CTRunGetStringRange获取run的Range
判断表情文字的location是否在run的Range
如果在 通过CTLineGetOffsetForStringIndex获取x的值 y的值为line原点的值
仅供参考
NSMutableAttributedString*abs = [[NSMutableAttributedString alloc] initWithString:text];
富文本:
字符间距正值间距加宽,负值间距变窄
typedefNS_ENUM(NSInteger, NSUnderlineStyle) {
NSUnderlineStyleNone =0x00,
NSUnderlineStyleSingle =0x01,
NSUnderlineStyleThickNS_ENUM_AVAILABLE(10_0,7_0) =0x02,
NSUnderlineStyleDoubleNS_ENUM_AVAILABLE(10_0,7_0) =0x09,
NSUnderlinePatternSolidNS_ENUM_AVAILABLE(10_0,7_0) =0x0000,
NSUnderlinePatternDotNS_ENUM_AVAILABLE(10_0,7_0) =0x0100,
NSUnderlinePatternDashNS_ENUM_AVAILABLE(10_0,7_0) =0x0200,
NSUnderlinePatternDashDotNS_ENUM_AVAILABLE(10_0,7_0) =0x0300,
NSUnderlinePatternDashDotDotNS_ENUM_AVAILABLE(10_0,7_0) =0x0400,
NSUnderlineByWordNS_ENUM_AVAILABLE(10_0,7_0) =0x8000
} NS_ENUM_AVAILABLE(10_0, 6_0);
和这三个任一个都好使,NSVerticalGlyphFormAttributeName,NSObliquenessAttributeName,NSExpansionAttributeName)
目前只有一个可用效果NSTextEffectLetterpressStyle (凸版印刷效果)
不能在UILabel和UITextField使用,只能用UITextView来进行,实现他的代理,在代理方法里面进行URL跳转
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange // 该方法返回YES就能打开URL,NO不做任何事情
上面的方法已经弃用了,取而代之的是下面的方法:
- (BOOL)textView:(UITextView*)textView shouldInteractWithURL:(NSURL*)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
注:
1.一定要实现UITextView的代理才能进行URL跳转
2.textView的editable属性修改为NO,在编辑时不可点击
//LRE: NSWritingDirectionLeftToRight|NSWritingDirectionEmbedding,
RLE: NSWritingDirectionRightToLeft|NSWritingDirectionEmbedding,
LRO: NSWritingDirectionLeftToRight|NSWritingDirectionOverride,
RLO: NSWritingDirectionRightToLeft|NSWritingDirectionOverride,
0为水平排版的字,1为垂直排版的字。注意,在iOS中, 总是以横向排版