textkit結構
textkit使用步驟
#Mark - 1. 自定義label --class CZLabel: UILabel---四個屬性
//1.屬性文本存儲
private lazy var textStorage = NSTextStorage()
//2.負責文本字形布局對象
private lazy var layoutManager = NSLayoutManager()
//3.設定文本繪制的範圍
private lazy var textContainer = NSTextContainer()
//4.屬性數組,儲存比對的範圍
private lazy var linkRanges = [NSRange]()
#Mark - 2. 重新init方法-- override init(frame: CGRect) {}
//0.開啟使用者互動
userInteractionEnabled = true
//1.textStorage接管label的屬性
if let attributedText = attributedText {}
//2.設定對象關系
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
#Mark - 3. 外界給label的text屬性指派 label.text = @"@好友,#健康#,....."
//重寫屬性的text方法--一旦label裡的内容發生變化,就可以讓textStorage相應變化
//1.段落處理--1.範圍 2.屬性 3.段落樣式
let attrStringM = addLineBreak(attributedText!)
//2.正則比對--1.清空原有 2.比對範圍 3.建立正則 4.比對 5.周遊比對結果,添加到屬性數組
regexLinkRanges(attrStringM)
//3.連接配接顔色設定---1.範圍 2.屬性 3.添加顔色 4.周遊屬性數組,改變顔色
addLinkAttribute(attrStringM)
//4.添加到textStorage
textStorage.setAttributedString(attrStringM)
//5.重新繪制
setNeedsDisplay()
#Mark - 4. textStorage字形和屬性發生變化時,通知NSLayoutManager重新布局文本
//MARK:3.設定布局--制定文本繪制區域
override func layoutSubviews() {
super.layoutSubviews()
//制定文本繪制區域
textContainer.size = bounds.size
}
#Mark - 5. 繪制textStorage的文本内容--不能調用super
override func drawTextInRect(rect: CGRect) {
let range = NSMakeRange(, textStorage.length)
//Glyphs--字形---CGPoint()從原點繪制,也就是右上角
layoutManager.drawGlyphsForGlyphRange(range, atPoint: CGPoint(x: ,y: ))
}
#Mark - 6. 使用者點選事件互動
//0.懶加載@ # URL的比對的正則法則 三個屬性數組
三步法:正規表達式 建立正則 比對 便利比對結果,添加到屬性數組
//1.擷取使用者點選的位置
let location = touches.first?.locationInView(self)
//2.擷取目前點中字元的索引
let index = layoutManager.glyphIndexForPoint(location, inTextContainer: textContainer)
//3.判斷index在哪個标記的range 範圍上
for range in atRange ?? [] {
if NSLocationInRange(index, range) {
let strSub = (textStorage.string as NSString).substringWithRange(range)
//進行結果處理
}
}
Swift使用
import UIKit
class ZYLabel: UILabel { //attributedText富文本
//MARK:2.重寫屬性text方法,可以在ViewController裡給文本指派
//一旦label裡的内容發生變化,就可以讓textStorage相應變化
override var text:String? {
didSet {
if attributedText == nil {
return
}
//換行處理屬性
let attrStringM = addLineBreak(attributedText!)
//換行後進行--正則比對
regexLinkRanges(attrStringM)
//換行後進行--連接配接顔色設定
addLinkAttribute(attrStringM)
//添加到textStorage
textStorage.setAttributedString(attrStringM)
//重新繪制
setNeedsDisplay()
}
}
///MARK: textKit的三個核心對象
//屬性文本存儲
private lazy var textStorage = NSTextStorage()
//負責文本字形布局對象
private lazy var layoutManager = NSLayoutManager()
//設定文本繪制的範圍
private lazy var textContainer = NSTextContainer()
private lazy var linkRanges = [NSRange]()
//純代碼接管Label
override init(frame: CGRect) {
super.init(frame: frame)
//0.開啟使用者互動
userInteractionEnabled = true
//1.textStorage接管label的屬性
if let attributedText = attributedText { //如果原有文本設定了attribute
textStorage.setAttributedString(attributedText)
}else if let text = text { //如果原有文本沒有設定attribute
textStorage.setAttributedString(NSAttributedString(string: text))
}else { //如果原有文本為nil
textStorage.setAttributedString(NSAttributedString(string: ""))
}
//2.設定對象關系
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
}
//MARK:1.Xib接管Label
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//0.開啟使用者互動
userInteractionEnabled = true
//1.準備文本内容---textStorage接管label的内容
if let attributedText = attributedText { //如果原有文本有attribute屬性
textStorage.setAttributedString(attributedText)
}else if let text = text { //如果原有文本沒有attribute屬性
textStorage.setAttributedString(NSAttributedString(string: text))
}else { //如果原有文本屬性為nil
textStorage.setAttributedString(NSAttributedString(string: ""))
}
//2.設定對象關系
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
}
/// MARK:2.1.段落樣式處理
private func addLineBreak(attrString: NSAttributedString) -> NSMutableAttributedString {
let attrStringM = NSMutableAttributedString(attributedString: attrString)
if attrStringM.length == {
return attrStringM
}
//從(0,0)點開始,也就是從text的第一個字元開始
var range = NSRange(location: , length: )
var attributes = attrStringM.attributesAtIndex(, effectiveRange: &range)
var paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSMutableParagraphStyle
//設定段落樣式--以字元分割,不以單詞分割
if paragraphStyle != nil {
//ByWordWrapping//按照單詞分割換行,保證換行時的單詞完整。
//ByCharWrapping按照字母換行,可能會在換行時将某個單詞拆分到兩行
paragraphStyle!.lineBreakMode = NSLineBreakMode.ByCharWrapping
} else {
// iOS 8.0 不能直接擷取段落的樣式
paragraphStyle = NSMutableParagraphStyle()
paragraphStyle!.lineBreakMode = NSLineBreakMode.ByCharWrapping
attributes[NSParagraphStyleAttributeName] = paragraphStyle
attrStringM.setAttributes(attributes, range: range)
}
return attrStringM
}
/// MARK:2.2.連接配接的attribute的顔色設定
private func addLinkAttribute(attrStringM: NSMutableAttributedString) {
if attrStringM.length == {
return
}
var range = NSRange(location: , length: )
var attributes = attrStringM.attributesAtIndex(, effectiveRange: &range)
attrStringM.addAttributes(attributes, range: range)
attributes[NSForegroundColorAttributeName] = UIColor.blueColor()
for range in linkRanges {
attrStringM.setAttributes(attributes, range: range)
}
}
/// MARK:2.3.正則法則--比對所有連接配接顔色:URL,#話題#,@好友---放到一個數組裡
private let patterns = ["((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\[email protected]#$%^&*+?:[a-zA-Z0-9]{3}_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\[email protected]#$%^&*+?:_/=<>]*)?)", "#.*?#", "@[\\u4e00-\\u9fa5a-zA-Z0-9_-]*"]
private func regexLinkRanges(attrString: NSAttributedString) {
//存儲所有的比對結果前,将原來的清空
linkRanges.removeAll()
//正則比對範圍--整個label
let regexRange = NSRange(location: , length: attrString.string.characters.count)
for pattern in patterns {
//建立正則
let regex = try! NSRegularExpression(pattern: pattern, options: .DotMatchesLineSeparators)
//比對
let results = regex.matchesInString(attrString.string, options:NSMatchingOptions(rawValue: ) , range: regexRange)
for range in results { //每一種正則法則可能比對到多個符合要求的對象如@張三 @李四 比對到兩個,結果是個數組
linkRanges.append(range.rangeAtIndex())
}
}
}
//MARK:3.設定布局--制定文本繪制區域
override func layoutSubviews() {
super.layoutSubviews()
//制定文本繪制區域
textContainer.size = bounds.size
}
//MARK:4.繪制textStorage的文本内容--不能調用super
override func drawTextInRect(rect: CGRect) {
let range = NSMakeRange(, textStorage.length)
//Glyphs--字形---CGPoint()從原點繪制,也就是右上角
layoutManager.drawGlyphsForGlyphRange(range, atPoint: CGPoint(x: ,y: ))
}
//MARK:5.使用者點選事件互動--處理不同比對内容天轉到不同界面
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//1.擷取使用者點選的位置--let location: CGPoint?
guard let location = touches.first?.locationInView(self) else {
return
}
//擷取目前點中字元的索引
let index = layoutManager.glyphIndexForPoint(location, inTextContainer: textContainer)
//判斷index在哪個标記的range 範圍上
for range in atRange ?? [] {
if NSLocationInRange(index, range) {
let strSub = (textStorage.string as NSString).substringWithRange(range)
print(strSub)
}
}
for range in jingRange ?? [] {
if NSLocationInRange(index, range) {
let strSub = (textStorage.string as NSString).substringWithRange(range)
print(strSub)
}
}
for range in urlRange ?? [] {
if NSLocationInRange(index, range) {
let strSub = (textStorage.string as NSString).substringWithRange(range)
NSNotificationCenter.defaultCenter().postNotificationName("webView", object: self, userInfo: ["urlString":strSub])
}
}
}
}
//MARK: 正規表達式處理結果
extension ZYLabel {
//傳回textStorage中的@肝健康公益 的range數組
var atRange:[NSRange]? {
//正規表達式[email protected]好友
let pattern = "@[\u{4e00}-\u{9fa5}]{0,}"
guard let regx = try? NSRegularExpression(pattern: pattern, options: []) else {
return nil
}
//多重比對--//let matchs: [NSTextCheckingResult]
let matchs = regx.matchesInString(textStorage.string, options: [], range: NSRange(location: ,length: textStorage.length))
//周遊數組
var ranges = [NSRange]()
for match in matchs {
ranges.append(match.rangeAtIndex())
}
return ranges
}
//傳回textStorage中的話題## 的range數組
var jingRange:[NSRange]? {
//正規表達式
let pattern = "#[\u{4e00}-\u{9fa5}]{0,}#"
guard let regx = try? NSRegularExpression(pattern: pattern, options: []) else {
return nil
}
//多重比對--//let matchs: [NSTextCheckingResult]
let matchs = regx.matchesInString(textStorage.string, options: [], range: NSRange(location: ,length: textStorage.length))
//周遊數組
var ranges = [NSRange]()
for match in matchs {
ranges.append(match.rangeAtIndex())
}
return ranges
}
//傳回textStorage中的URL網址的range數組
var urlRange:[NSRange]? {
//正規表達式
let pattern = "((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\[email protected]#$%^&*+?:[a-zA-Z0-9_/=<>]]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\[email protected]#$%^&*+?:_/=<>]*)?)"
guard let regx = try? NSRegularExpression(pattern: pattern, options: []) else {
return nil
}
//多重比對--//let matchs: [NSTextCheckingResult]
let matchs = regx.matchesInString(textStorage.string, options: [], range: NSRange(location: ,length: textStorage.length))
//周遊數組
var ranges = [NSRange]()
for match in matchs {
ranges.append(match.rangeAtIndex())
}
return ranges
}
}