在我們iOS項(xiàng)目開發(fā)中,我們經(jīng)常會(huì)遇到圖文混排的情況,那么什么是圖文混排呢?

創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,先為伊金霍洛等服務(wù)建站,伊金霍洛等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為伊金霍洛企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
那么什么是圖文混排呢?
在這里我給大家舉個(gè)例子大家就明白了,例如我們在微博類,社交聊天應(yīng)用中常常會(huì)遇到各種表情,各種鏈接的解析。問題來了,圖文混排的形式有哪些呢?
圖文混排的形式:
1.富文本(attributeString)
我們可以采用attributeString來進(jìn)行圖文混排.例如一個(gè)文字上插入一個(gè)圖片
什么是coreText?
iOS/OSX中用于描述富文本的類是NSAttributedString,顧名思義,它比NSString多了Attribute的概念。它可以包含很多屬性,粗體,斜體,下劃線,顏色,背景色等等,每個(gè)屬性都有其對應(yīng)的字符區(qū)域。在OSX上我們只需解析完畢相應(yīng)的數(shù)據(jù),準(zhǔn)備好NSAttributedString即可,底層的繪制完全可以交給相應(yīng)的控件完成。但是在iOS上就沒有這么方便,想要繪制Attributed String就需要用到CoreText了。(當(dāng)然iOS6之后已經(jīng)有AttributedLabel了。)
使用CoreText進(jìn)行NSAttributedString的繪制,最重要的兩個(gè)概念就是CTFrameSetter和CTFrame。
其中CTFramesetter是由CFAttributedString(NSAttributedString)初始化而來,可以認(rèn)為它是CTFrame的一個(gè)Factory,通過傳入CGPath生成相應(yīng)的CTFrame并使用它進(jìn)行渲染:直接以CTFrame為參數(shù)使用CTFrameDraw繪制或者從CTFrame中獲取CTLine進(jìn)行微調(diào)后使用CTLineDraw進(jìn)行繪制。
一個(gè)CTFrame是由一行一行的CLine組成,每個(gè)CTLine又會(huì)包含若干個(gè)CTRun(既字形繪制的最小單元),通過相應(yīng)的方法可以獲取到不同位置的CTRun和CTLine,以實(shí)現(xiàn)對不同位置touch事件的響應(yīng)。
ios7 開始,功能強(qiáng)大,簡單易用,也可以進(jìn)行圖文混排. TextKit并沒有新增的類,他是在原有的文本顯示控件上的封裝,可以使用平時(shí)我們最喜歡使用的UILabel,UITextField,UITextView里面就可以使用了。現(xiàn)在來詳細(xì)介紹一下.
1).NSAtrributedString
這是所有TextKit的載體,所有的信息都會(huì)輸入到NSAttributedString里面,然后將這個(gè)String輸入到Text控件里面就可以顯示了。
2).NSTextAttachment
iOS7新增的類,作為文本的附件,可以放文件,可以放數(shù)據(jù),以 NSAttachmentAttributeName這個(gè)key放入NSAttributedString里面,在表情混排這里,我們將放入image。
3).重載NSTextAttachment
本來是可以直接使用NSTextAttachment,但是我們需要根據(jù)文字大小來改變表情圖片的大小,于是我們需要重載NSTextAttachment,NSTextAttachment實(shí)現(xiàn)了NSTextAttachmentContainer,可以給我們改變返回的圖像,圖像的大小。
利用UIWebView加載HTML實(shí)現(xiàn)圖文混排
但是注意:UIWebView本身有內(nèi)存問題,占用內(nèi)存相比較而較大不推薦,但是使用比較靈活,
iOS實(shí)現(xiàn)圖文混排的兩個(gè)方法
如果你想自定義文本的布局,例如像QQ、微信這樣的應(yīng)用中使用表情,那你多半會(huì)用到CoreText,CoreText是iOS、OSX平臺(tái)的文本處理低層的框架, 可以實(shí)現(xiàn)任意的文字編排,更多詳細(xì)信息請戳官方文檔,一般來說, 我們們用下面的代碼來實(shí)現(xiàn)圖文混排:
text = [[NSMutableAttributedString alloc] initWithString:@""];
NSAttributedString *txt1 = [[NSAttributedString alloc] initWithString:@"測試"];
[text appendAttributedString:txt1];
[txt1 release];
CTRunDelegateCallbacks callback;
callback.version = kCTRunDelegateVersion1; //必須指定,否則不會(huì)生效,沒有回調(diào)產(chǎn)生。
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: @"結(jié)束"] 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];
一直以來,我認(rèn)為只有這種方法實(shí)現(xiàn)。好吧,其實(shí)我沒有想過有沒有其它實(shí)現(xiàn)方法的問題。直到有一天看類似效果的代碼時(shí)驚奇的發(fā)現(xiàn):怎么 沒有CTRunDelegate? 于是就仔細(xì)想了一下這個(gè)問題,創(chuàng)建CTFrame的時(shí)候會(huì)指定一個(gè)path,通常這個(gè)path我會(huì)使用一個(gè)CGRect完事,然后在 有圖片的地方使用CTRunDelegate處理一下,但其實(shí)完全可以使用CGMutablePath來畫出一塊不規(guī)則的文本路徑,比如:這樣,就可以在預(yù)定的位置畫圖片了,而不用會(huì)CTRunDelegate來特殊處理,這種方式比較適合圖片位置固定的應(yīng)用。
轉(zhuǎn)自madongsheng
可以使用CoreText, 但學(xué)習(xí)的話,也要花不短的時(shí)間。
或者你可以換一種思路, 例如上面的內(nèi)容,在接口端解析內(nèi)容返回一個(gè)內(nèi)容數(shù)組。文字是一組,一張圖是一組。這樣在ios端顯示時(shí),只需要解析這個(gè)數(shù)組就可以了。如果是文本用UILabel展示,如果是圖片鏈接,用UIImageView展示。
圖文混排
CTFrameRef textFrame // coreText 的 frame
CTLineRef line // coreText 的 line
CTRunRef run // line 中的部分文字
相關(guān)方法:
CFArrayRef CTFrameGetLines (CTFrameRef frame ) //獲取包含CTLineRef的數(shù)組
void CTFrameGetLineOrigins(
CTFrameRef frame,
CFRange range,
CGPoint origins[] ) //獲取所有CTLineRef的原點(diǎn)
CFRange CTLineGetStringRange (CTLineRef line ) //獲取line中文字在整段文字中的Range
CFArrayRef CTLineGetGlyphRuns (CTLineRef line ) //獲取line中包含所有run的數(shù)組
CFRange CTRunGetStringRange (CTRunRef run ) //獲取run在整段文字中的Range
CFIndex CTLineGetStringIndexForPosition(
CTLineRef line,
CGPoint position ) //獲取點(diǎn)擊處position文字在整段文字中的index
CGFloat CTLineGetOffsetForStringIndex(
CTLineRef line,
CFIndex charIndex,
CGFloat* secondaryOffset ) //獲取整段文字中charIndex位置的字符相對line的原點(diǎn)的x值
主要步驟:
1)計(jì)算并存儲(chǔ)文字中保含的所有表情文字及其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) 根據(jù)保存的表情文字的Range計(jì)算表情圖片的Frame
textFrame 通過CTFrameGetLines 獲取所有l(wèi)ine的數(shù)組 lineArray
遍歷lineArray中的line通過CTLineGetGlyphRuns獲取line中包含run的數(shù)組 runArray
遍歷runArray中的run 通過CTRunGetStringRange獲取run的Range
判斷表情文字的location是否在run的Range
如果在 通過CTLineGetOffsetForStringIndex獲取x的值 y的值為line原點(diǎn)的值
僅供參考
NSMutableAttributedString*abs = [[NSMutableAttributedString alloc] initWithString:text];
富文本:
字符間距正值間距加寬,負(fù)值間距變窄
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);
和這三個(gè)任一個(gè)都好使,NSVerticalGlyphFormAttributeName,NSObliquenessAttributeName,NSExpansionAttributeName)
目前只有一個(gè)可用效果NSTextEffectLetterpressStyle (凸版印刷效果)
不能在UILabel和UITextField使用,只能用UITextView來進(jìn)行,實(shí)現(xiàn)他的代理,在代理方法里面進(jìn)行URL跳轉(zhuǎn)
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange ? ? // 該方法返回YES就能打開URL,NO不做任何事情
上面的方法已經(jīng)棄用了,取而代之的是下面的方法:
- (BOOL)textView:(UITextView*)textView shouldInteractWithURL:(NSURL*)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
注:
1.一定要實(shí)現(xiàn)UITextView的代理才能進(jìn)行URL跳轉(zhuǎn)
2.textView的editable屬性修改為NO,在編輯時(shí)不可點(diǎn)擊
//LRE: NSWritingDirectionLeftToRight|NSWritingDirectionEmbedding,?
RLE: NSWritingDirectionRightToLeft|NSWritingDirectionEmbedding,
LRO: NSWritingDirectionLeftToRight|NSWritingDirectionOverride,?
RLO: NSWritingDirectionRightToLeft|NSWritingDirectionOverride,
0為水平排版的字,1為垂直排版的字。注意,在iOS中, 總是以橫向排版
新聞名稱:ios開發(fā)圖文混排,ios圖文混排第三方
本文地址:http://chinadenli.net/article45/dsgcshi.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、電子商務(wù)、Google、商城網(wǎng)站、響應(yīng)式網(wǎng)站、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)