圖文混排初體驗(yàn)---TextKit框架和屬性字符串

? ? ? ?圖文混排版可以說(shuō)在很多地方都常用茂契,但真正想實(shí)現(xiàn)一些高級(jí)的效果還是要耗費(fèi)不少的精力去系統(tǒng)的學(xué)習(xí)的。圖文混排實(shí)現(xiàn)的方式一般有兩種方式:基于Html5和蘋(píng)果提供的框架(TextKit或者CoreText)慨绳。账嚎。

? ? ? ?先簡(jiǎn)單說(shuō)下第一種方式,一般是通過(guò)OC和JS相互調(diào)用實(shí)現(xiàn)的儡蔓,其中OC和JS相互調(diào)用可以有三種實(shí)現(xiàn)方式分別是UIWebVIew郭蕉,WKWebView,以及JavaScriptCore三種方式喂江。UIWebView雖然比較穩(wěn)定但是占用的資源比較多召锈,WKWebView占用資源相對(duì)于UIWebView來(lái)說(shuō)很小,但是目前來(lái)看依然沒(méi)有得到較為廣泛的應(yīng)用获询,原因在于WKWebView在穩(wěn)定性方面依然存在一些細(xì)小的問(wèn)題涨岁。三種簡(jiǎn)單的調(diào)用方式直接提供給源碼給大家看一下https://github.com/ZhengYaWei1992/1.JSAndOC,后續(xù)的話會(huì)在之后的簡(jiǎn)書(shū)文章中更新吉嚣。Tips:更新介紹的內(nèi)容含有JS和OC相互調(diào)用傳參問(wèn)題梢薪、第三方模板框架使用問(wèn)題、以及類似網(wǎng)易新聞這種圖文混排界面是如何實(shí)現(xiàn)的尝哆。之前有些過(guò)一個(gè)小小的實(shí)現(xiàn)類似網(wǎng)易新聞詳情界面實(shí)現(xiàn)的小Demo秉撇,之后一并分享給大家。這和不是今天的最主要話題秋泄,扯的有點(diǎn)多了琐馆。

? ? ? ? 接下來(lái)先說(shuō)下第二種實(shí)現(xiàn)方式,TextKit或者CoreText恒序。注意是或者,其實(shí)之前有段時(shí)間非常強(qiáng)烈的認(rèn)真學(xué)習(xí)過(guò)一寫(xiě)和CoreText相關(guān)的東西滋饲,但是學(xué)者學(xué)著真是無(wú)力吐槽了屠缭。發(fā)現(xiàn)實(shí)現(xiàn)各簡(jiǎn)單的圖文混排效果實(shí)在是太繁瑣了勿她,后來(lái)果斷放棄阵翎。不過(guò)現(xiàn)在發(fā)現(xiàn)郭卫,以前放棄學(xué)習(xí)CoreText確實(shí)是明智之舉贰军,因?yàn)閕OS7之后有個(gè)比它更好使用词疼,更容易實(shí)現(xiàn)圖文混排效果的框架TextKit(后來(lái)才知道這個(gè)框架贰盗,之前走了不少的彎路)。

? ? ? ?先來(lái)展示下陋率,之前自己封裝的一個(gè)圖文混排效果圖瓦糟∑姓悖可以自動(dòng)識(shí)別表情芍耘,URL和昵稱,當(dāng)然還可以識(shí)別更多(最主要的是正則表達(dá)式的應(yīng)用)斋竞。使用起來(lái)也十分簡(jiǎn)單坝初,直接繼承與ZWLabel這個(gè)類就可以鳄袍。暫時(shí)不放代碼拗小,后續(xù)再繼續(xù)更新哀九,先看看效果。


? ? ? ? 本文先從實(shí)現(xiàn)一個(gè)簡(jiǎn)單的自定義識(shí)別網(wǎng)址的圖文混排開(kāi)始呼胚。初體驗(yàn)之效果圖:

? ? ? ?學(xué)習(xí)TextKit的第一步:是需要認(rèn)識(shí)里面的三個(gè)類NSTextStorage、NSLayoutManager呼盆、NSTextContainer分別的作用和職責(zé)。三者的職責(zé):NSTextStorage負(fù)責(zé)文本存儲(chǔ)常遂,NSLayoutManager負(fù)責(zé)文本字形布局克胳,NSTextContainer設(shè)定文本繪制的范圍(一般開(kāi)發(fā)很少用漠另,定義一個(gè)矩形區(qū)域笆搓,也可以定義一個(gè)排除路徑)满败。第二步:要認(rèn)清實(shí)現(xiàn)自定義識(shí)別網(wǎng)址的Label的本質(zhì)是:使用textKit接替label的底層實(shí)現(xiàn)算墨。第三步:實(shí)現(xiàn)自定義識(shí)別網(wǎng)址URL的步驟:1.使用textKit接替label的底層實(shí)現(xiàn)? ---繪制textStorage的文本內(nèi)容汁雷。2.使用正則表達(dá)式過(guò)濾URL侠讯。3.解決交互問(wèn)題厢漩。

? ? ? ?開(kāi)始上主菜代碼了,先來(lái)個(gè)簡(jiǎn)單的Swift代碼。具體Demo鏈接宵膨,請(qǐng)?jiān)谖恼履┪膊榭础?/p>

1.ZWLabel.swift中

```

import UIKit

class ZWLabel: UILabel { ? ?

? ? ? ? lazy var textStorage = NSTextStorage() ??

? ? ? ? lazy var layoutManager = NSLayoutManager() ??

? ? ? ? ?lazy var textContainer = NSTextContainer()? ??

? ? ? ? override var text: String?{? ? ? ?

? ? ? ? ? ? ?didSet{ ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? prepareTextContent() ? ? ? ?

? ? ? ? ? ? ? ? }? ?

? ? ? ? ? } ? ? ??

? ? ? ?override init(frame: CGRect) {? ? ? ??

? ? ? ? ? ? ? super.init(frame: frame)? ? ? ?

? ? ? ? ? ? ? backgroundColor = UIColor.orange? ? ? ??

? ? ? ? ? ? ? ?prepareTextSystem()??

? ? ? ? }? ? ? ?

? ? ? ?required init?(coder aDecoder: NSCoder) { ? ??

? ? ? ? ? ? ? ?super.init(coder: aDecoder)? ? ??

? ? ? ? ? ? ? backgroundColor = UIColor.orange? ? ? ??

? ? ? ? ? ? ? prepareTextSystem()??

? ? ? ?} ? ? ? ?

? ? override func drawText(in rect: CGRect){? ? ? ?

? ? ? ? ? ? ? ?//消除父類的繪制狐树,這里我們自己繪制? ? ? // super.drawText(in: rect)? ?

? ? ? ? ? ? ? let range = NSRange(location: 0, length: textStorage.length) ? ? ? ?

? ? ? ? ? ? ?layoutManager.drawBackground(forGlyphRange: range, at:? CGPoint())? ??

? ? ? ? ? ? ? //繪制Glyph字形? ? CGPoint()表示從原點(diǎn)開(kāi)始繪制 ? ??

? ? ? ? ? ? ?layoutManager.drawGlyphs(forGlyphRange: range, at: CGPoint())? ? ? ? ?

? ? ?} ? ??

? ? ? override func layoutSubviews() {? ? ? ?

? ? ? ? ? ? ? ? ? ?super.layoutSubviews()? ? ? ??

? ? ? ? ? ? ? ? //指定文本的繪制區(qū)域? ? ? ??

? ? ? ? ? ? ? ?textContainer.size = bounds.size ??

? ? ?}? ? ?

? ? override func touchesBegan(_ touches: Set, with event: UIEvent?) {

? ? ? ? ? ? //1.獲取用戶點(diǎn)擊的位置

? ? ? ? ? ? ? //first?相當(dāng)于OC的anyObject

? ? ? ? ? ?guard let location = touches.first?.location(in: self)else{

? ? ? ? ? ? ? ? ? ?return

? ? ? ? ? }

? ? ? ? ? ?print("點(diǎn)我了\(location)")

? ? ? ? //2.記錄當(dāng)前點(diǎn)中字符的索引

? ? ? ? ?let idx = layoutManager.glyphIndex(for: location, in: textContainer)

? ? ? ? ? print("點(diǎn)我了\(idx)")

? ? ? ? ? ?//3.判斷idx是否在url的range范圍內(nèi),如果在就高亮

? ? ? for r in urlRanges ?? []{

? ? ? ? ? ? //NSRanege跳入頭文件可以看見(jiàn)次方法 NSLocationInRange

? ? ? ? ? ? if NSLocationInRange(idx, r){

? ? ? ? ? ? ? print("需要高亮")

? ? ? ? ? ? //修改文本的字體屬性

? ? ? ? ? ? textStorage.addAttributes([NSForegroundColorAttributeName:UIColor.blue], range: r)

? ? ? ? ? ? ?//如果需要重新繪制野哭,需要調(diào)用setNeedsDisplay方法

? ? ? ? ? ?setNeedsDisplay()

? ? ? ? ? ?}else{

? ? ? ? ? ? ? ? ? ? print("沒(méi)點(diǎn)到")

? ? ? ? ? ?}

? ? ? ? ?}

? ?}

}

// MARK: - 設(shè)置textKit核心對(duì)象

private extension ZWLabel{

? ? ? ? ?///準(zhǔn)備文本系統(tǒng)

? ? ? ? ?func prepareTextSystem(){

? ? ? ? ? ? ? ? ? //0.開(kāi)啟用戶交互

? ? ? ? ? ? ? ? ? ? ?isUserInteractionEnabled = true

? ? ? ? ? ? ? ? ?//1.準(zhǔn)備文本內(nèi)容

? ? ? ? ? ? ? ? ?prepareTextContent()

? ? ? ? ? ? ? ? ? //2.設(shè)置對(duì)象的關(guān)系? ---一般都是固定的寫(xiě)法

? ? ? ? ? ? ? ? textStorage.addLayoutManager(layoutManager)

? ? ? ? ? ? ? ? ? ? layoutManager.addTextContainer(textContainer)

? ? ? ? ? ? ?}

? ? ? ? ? ?/// 準(zhǔn)備文本內(nèi)容拨黔,接管label內(nèi)容

? ? ? ? ?func prepareTextContent(){

? ? ? ? ? ? ? ? if let attributedText = attributedText{

? ? ? ? ? ? ? ? textStorage.setAttributedString(attributedText)

? ? ? ? ?}else if let text = text{

? ? ? ? ? ? ? ? textStorage.setAttributedString(NSAttributedString(string: text))

? ? ? ? }else{

? ? ? ? ? ? ? textStorage.setAttributedString(NSAttributedString(string: ""))

? ? ? ? ?}

? ? ? ? ? ?// print(urlRanges)

? ? ? ?//遍歷范圍數(shù)組篱蝇,設(shè)置URL文字的屬性

? ? ? ? ?for r in urlRanges ?? []{

? ? ? ? ? textStorage.addAttributes([NSForegroundColorAttributeName:UIColor.red,NSBackgroundColorAttributeName:UIColor.init(white: 0.9, alpha: 1.0)], range: r)

? ? ? ? ? ? }

? ? ?}

}

// MARK: - 正則表達(dá)式獲取url范圍

extension ZWLabel{

? ? ? ? ? ? ?///返回textStorage中的URL range數(shù)組

? ? ? ? ? ? ?var urlRanges: [NSRange]?{

? ? ? ? ? ? ? ? ? ? ? ?let pattern = "[a-zA-Z]*://[a-zA-Z0-9/\\.]*"

? ? ? ? ? ? ? ? ? ? ?guard let regx = try? NSRegularExpression(pattern: pattern, options: [])else{

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return nil

? ? ? ? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ? ? //2.多重匹配零截,因?yàn)橐粋€(gè)字符串中可能包含多個(gè)url

? ? ? ? ? ? ? ? ? let matches = regx.matches(in: textStorage.string, options: [], range:NSRange(location: 0, length: textStorage.length))

? ? ? ? ? ? ? ? //3.遍歷數(shù)組涧衙,生成range數(shù)組

? ? ? ? ? ? ? ?var ranges = [NSRange]()

? ? ? ? ? ? ? ?for m in matches{

? ? ? ? ? ? ? ? ? ? ? ? ranges.append(m.rangeAt(0))

? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ? return ranges

? ? ?}

}

```

2.外部調(diào)用形式

```

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var label: ZWLabel!

? ? ? ? ?override func viewDidLoad() {

? ? ? ? ? ? ? ? ? super.viewDidLoad()

? ? ? ? ? ? ? ? ? ?label.text = "請(qǐng)點(diǎn)擊http://www.baidu.com"

? ? ? ? ? ?}

}

```

當(dāng)然以上代碼只是簡(jiǎn)單的認(rèn)識(shí)一下TextKit這個(gè)框架,實(shí)際上想真正的完成一個(gè)自動(dòng)識(shí)別URL稚虎,添加表情圖片的圖文混排label還是有很多事情要做的蠢终,比如還要考慮行高蜕径、字體間距、點(diǎn)擊時(shí)顯示的效果等因素梦染。實(shí)際上添加上表情就是相對(duì)比較簡(jiǎn)單的事情了帕识,主要是通過(guò)屬性文本肮疗,代碼如下(具體實(shí)現(xiàn)自行添加):

```

//0.圖片附件

let attachment = NSTextAttachment()

attachment.image = #imageLiteral(resourceName: "d_aini.png")

let height = label.font.lineHeight

attachment.bounds = CGRect(x: 0, y: -4, width: height, height: height)

//1.屬性文本

let imageStr =? NSAttributedString(attachment:attachment )

//2.可變的圖文字符串 ----注意:這里可變的要用NSMutableAttributedString伪货,用var不起作用

let attStrM = NSMutableAttributedString(string: "我")

attStrM.append(imageStr)

attStrM.append(NSAttributedString(string: "99!"))

//3.設(shè)置label

label.attributedText = attStrM

```

Demo參考鏈接地址:https://github.com/ZhengYaWei1992/BaseTextKit

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蒙挑,一起剝皮案震驚了整個(gè)濱河市忆蚀,隨后出現(xiàn)的幾起案子馋袜,更是在濱河造成了極大的恐慌舶斧,老刑警劉巖捧毛,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呀忧,死亡現(xiàn)場(chǎng)離奇詭異而账,居然都是意外死亡泞辐,警方通過(guò)查閱死者的電腦和手機(jī)吹缔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)厢塘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晚碾,“玉大人格嘁,你說(shuō)我怎么就攤上這事糕簿。” “怎么了新症?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵荚醒,是天一觀的道長(zhǎng)侯繁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)贮竟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任惰拱,我火速辦了婚禮偿短,結(jié)果婚禮上昔逗,老公的妹妹穿的比我還像新娘勾怒。我一直安慰自己,他們只是感情好控硼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布泽论。 她就那樣靜靜地躺著,像睡著了一般卡乾。 火紅的嫁衣襯著肌膚如雪翼悴。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天幔妨,我揣著相機(jī)與錄音鹦赎,去河邊找鬼。 笑死误堡,一個(gè)胖子當(dāng)著我的面吹牛古话,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锁施,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼悉抵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起态蒂,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎募舟,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸟悴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年震贵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了之碗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片式塌。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祭往,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出离钝,到底是詐尸還是另有隱情鲤竹,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布错负,位于F島的核電站粒褒,受9級(jí)特大地震影響月杉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一炊甲、第九天 我趴在偏房一處隱蔽的房頂上張望菱父。 院中可真熱鬧同仆,春花似錦岁忘、人聲如沸蝠筑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)聋呢。三九已至蛹稍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抗斤,地道東北人徒像。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓厨姚,卻偏偏與公主長(zhǎng)得像拭抬,于是被迫代替她去往敵國(guó)和親算凿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容