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

来源:互联网 发布:网络五层模型每层功能 编辑:程序博客网 时间:2024/06/08 22:13

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.添加到textStoragetextStorage.setAttributedString(attrStringM)//5.重新绘制setNeedsDisplay()#Mark - 4. textStorage字形和属性发生变化时,通知NSLayoutManager重新布局文本//MARK:3.设置布局--制定文本绘制区域override func layoutSubviews() {    super.layoutSubviews()    //制定文本绘制区域    textContainer.size = bounds.size}#Mark - 5. 绘制textStorage的文本内容--不能调用superoverride 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 UIKitclass 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 == 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    }}
0 0
原创粉丝点击