您的位置:首页 > 移动开发 > IOS开发

iOS 开发 富文本详解之TextKit详解

2016-11-30 09:03 639 查看

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(0, textStorage.length)
//Glyphs--字形---CGPoint()从原点绘制,也就是右上角
layoutManager.drawGlyphsForGlyphRange(range, atPoint: CGPoint(x: 0,y: 0))
}

#Mark - 6. 用户点击事件交互
//0.懒加载@ # URL的匹配的正则法则 三个属性数组
三步法:1.正则表达式  2.创建正则  3.匹配  4.便利匹配结果,添加到属性数组
//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.设置对象关系
textSto
12174
rage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
}

/// MARK:2.1.段落样式处理
private func addLineBreak(attrString: NSAttributedString) -> NSMutableAttributedString {
let attrStringM = NSMutableAttributedString(attributedString: attrString)
if attrStringM.length == 0 {
return attrStringM
}
//从(0,0)点开始,也就是从text的第一个字符开始
var range = NSRange(location: 0, length: 0)
var attributes = attrStringM.attributesAtIndex(0, 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 == 0 {
return
}
var range = NSRange(location: 0, length: 0)
var attributes = attrStringM.attributesAtIndex(0, 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\\.\\-~!@#$%^&*+?:[a-zA-Z0-9]{3}_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)", "#.*?#", "@[\\u4e00-\\u9fa5a-zA-Z0-9_-]*"]
private func regexLinkRanges(attrString: NSAttributedString) {
//存储所有的匹配结果前,将原来的清空
linkRanges.removeAll()
//正则匹配范围--整个label
let regexRange = NSRange(location: 0, 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: 0) , range: regexRange)
for range in results {      //每一种正则法则可能匹配到多个符合要求的对象如@张三  @李四 匹配到两个,结果是个数组
linkRanges.append(range.rangeAtIndex(0))
}
}
}

//MARK:3.设置布局--制定文本绘制区域
override func layoutSubviews() {
super.layoutSubviews()
//制定文本绘制区域
textContainer.size = bounds.size
}

//MARK:4.绘制textStorage的文本内容--不能调用super
override func drawTextInRect(rect: CGRect) {
let range = NSMakeRange(0, textStorage.length)
//Glyphs--字形---CGPoint()从原点绘制,也就是右上角
layoutManager.drawGlyphsForGlyphRange(range, atPoint: CGPoint(x: 0,y: 0))
}

//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]? {
//正则表达式--@好友
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: 0,length: textStorage.length))
//遍历数组
var ranges = [NSRange]()
for match in matchs {
ranges.append(match.rangeAtIndex(0))
}
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: 0,length: textStorage.length))
//遍历数组
var ranges = [NSRange]()
for match in matchs {
ranges.append(match.rangeAtIndex(0))
}
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\\.\\-~!@#$%^&*+?:[a-zA-Z0-9_/=<>]]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)"
guard let regx = try? NSRegularExpression(pattern: pattern, options: []) else {
return nil
}
//多重匹配--//let matchs: [NSTextCheckingResult]
let matchs = regx.matchesInString(textStorage.string, options: [], range: NSRange(location: 0,length: textStorage.length))
//遍历数组
var ranges = [NSRange]()
for match in matchs {
ranges.append(match.rangeAtIndex(0))
}
return ranges
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐