关闭

XZ_Swift之TextKit类实现把文字变成别的颜色且可交互

标签: 文本高亮把某段文字变成别的颜色某段文字还可以跟用户交互图文混排链接高亮和点击进行跳转
240人阅读 评论(0) 收藏 举报
分类:

TextKit类结构介绍

TextKit 是 iOS7推出的,面向对象的文本处理框架;

借用一张图来介绍一下:

图中真正的引用关系是实线的引用,反方向的虚线是弱引用

具体解读

1>NSTextStorage 负责存储属性文本;
2>NSLayoutManager 用来布局的,负责绘制 3个方法: 绘制字形、绘制背景(比如有些要设置底色的)、布局字形(知道用户点到哪里,点到第几个字符了);
3>NSTextContainer 一般不怎么用,它只是用来指定区域,在 layoutSubviews 设置关联就可以,就是来设定文本显示区域的

TextKit作用:

把某段文字变成别的颜色,把一段文本高亮,且这段文字还可以跟用户交互,这也是图文混排里面非常重要的一个技能
实现效果:点击到网址部分,文字高亮,跳转到下一个页面

实现思路:
1.使用 TextKit 接管 Label 的底层实现 - ‘绘制’ textStorage 的文本内容
2.使用正则表达式过滤 URL
3.交互
代码实现:
class XZLabel: UILabel {
    
    // MARK: - 重写属性 - 进一步体会 TextKit 接管底层的实现
    // 一旦内容变化,需要让 textStorage 响应变化!
    override var text: String? {
        didSet { // 重写准备文本内容
            prepareTextContent()
        }
    }
    
    override var attributedText: NSAttributedString? {
        didSet { // 重写准备文本内容
            prepareTextContent()
        }
    }
    
    override var font: UIFont! {
        didSet { // 重写准备文本内容
            prepareTextContent()
        }
    }
    
    override var textColor: UIColor! {
        didSet { // 重写准备文本内容
            prepareTextContent()
        }
    }
    
    // MARK: - 构造函数
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        prepareTextSystem()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        prepareTextSystem()
    }
    
    // MARK: - 和 URL 的文本交互
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 1.获取用户点击的位置
        guard let location = touches.first?.location(in: self) else {
            return
        }
        
        // 2.获取当前点中字符的索引
        let idx = layoutManager.glyphIndex(for: location, in: textContainer)
        
        print("点我了 \(idx)")
        
        // 3.判断 idx 是否在 urls 的 ranges 范围内,如果在,就高亮
        for rang in urlRanges ?? []{
            
            // 返回 bool 值
            if NSLocationInRange(idx, rang) {
                // 如果在范围内,需要高亮
                // 修改文本的字体属性
            textStorage.addAttributes([NSAttributedStringKey.foregroundColor : linkTextColor], range: rang)
                
                // 需要调用 setNeedsDisplay 函数重绘,但是不是 drawRect
                setNeedsDisplay()
            }else {
                print("没戳中")
            }
        }
    }
    
    /// 绘制文本
    /**
     - 在 iOS 中绘制工作是类似于‘油画’似的,后绘制的内容,会把之前绘制的内容覆盖!
     - 尽量避免使用带透明度的颜色,会严重影响性能!
     - UILabel 默认不能实现垂直顶部对齐,使用 TextKit 的当前方法可以实现
     */
    override func drawText(in rect: CGRect) {
        
        let range = NSRange(location: 0, length: textStorage.length)
        
        // 绘制背景 - 注意:绘制背景要放在绘制'字形'前面,否则会覆盖 '字形'
        layoutManager.drawBackground(forGlyphRange: range, at: CGPoint())
        
        // 绘制 Glyphs 字形
        layoutManager.drawGlyphs(forGlyphRange: range, at: CGPoint())
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // 指定文本绘制的区域
        textContainer.size = bounds.size
    }
    
    /// 链接颜色
    public var linkTextColor = UIColor.blue
    /// 选中背景色
    public var selectedBackgroundColor = UIColor.init(white: 0.8, alpha: 1.0)
    
    // MARK: - TextKit 的核心对象
    /// 属性文本存储
    private lazy var textStorage = NSTextStorage()
    /// 负责文本'字形'布局
    private lazy var layoutManager = NSLayoutManager()
    /// 设置文本绘制的范围
    private lazy var textContainer = NSTextContainer()
}

// MARK: - 正则表达式函数
private extension XZLabel {
    
    /// 返回 textStorage 中的 URL range 数组
    var urlRanges : [NSRange]? {
        // 1. url 的正则表达式
        let pattern = "[a-zA-Z]*://[a-zA-Z0-9/\\.]*"
        
        guard let regx = try? NSRegularExpression(pattern: pattern, options: []) else {
            return nil
        }
        
        // 2.多重匹配
        let matches = regx.matches(in: textStorage.string, options: [], range: NSRange(location: 0, length: textStorage.length))
        
        // 3.遍历数组,生成 range 的数组
        var ranges = [NSRange]()
        
        for m in matches {
            ranges.append(m.range(at: 0))
        }
        
        return ranges
    }
}

// MARK: - 设置 TextKit 核心对象
private extension XZLabel {
    
    /// 准备文本系统
    func prepareTextSystem() {
        // 0.开启用户交互
        isUserInteractionEnabled = true
        
        // 1. 准备文本内容
        prepareTextContent()
        
        // 2.设置对象的关系
        textStorage.addLayoutManager(layoutManager)
        layoutManager.addTextContainer(textContainer)
    }
    
    /// 准备文本内容 - 使用 textStorage 接管 label 的内容
    func prepareTextContent() {
        
        if let attributedText = attributedText {
            textStorage.setAttributedString(attributedText)
        }else if let text = text {
            textStorage.setAttributedString(NSAttributedString(string: text))
        }else {
            textStorage.setAttributedString(NSAttributedString(string: ""))
        }
        
        // 范围是 - Optional([{3, 20}])
        print("范围是 - \(urlRanges)")
        
        // 遍历范围数组,设置 URL 文字属性,URL 特殊显示
        for rang in urlRanges ?? [] {
            textStorage.addAttributes(
                [
                    NSAttributedStringKey.foregroundColor : UIColor.red,
                    NSAttributedStringKey.backgroundColor : selectedBackgroundColor
                ],
                range: rang)
        }
    }



0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

关于TextView中,设置指定部分文字改变颜色,和指定部分文字点击事件

关于TextView中,设置指定部分文字改变颜色,和指定部分文字点击事件
  • u013676055
  • u013676055
  • 2016-03-05 16:16
  • 5768

Android中设置部分字体的颜色改变,并且能点击

Android中设置部分字体的颜色改变,一共有两种方法: 1, 使用SpannableStringBuilder来实现 2, 使用 html 来实现 Andorid中设置部分字体的颜色改变,且能点...
  • WOW875620051
  • WOW875620051
  • 2016-07-23 01:16
  • 1909

java实现渐变效果工具

package gradient; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; ...
  • ycb1689
  • ycb1689
  • 2013-11-14 09:25
  • 3622

使用TextKit自定义UILabel,使用正则匹配链接(不同颜色显示)等,提高Label性能(Xcode 8.2.1 (8C1002) swift3最新语法)

// // TGLabel.swift // sinaweibo // // Created by targetcloud on 2017/2/27. // Copyright © 2017年...
  • callzjy
  • callzjy
  • 2017-02-27 23:04
  • 717

XZ_Swift之使用苹果原生UIRefreshControl实现下拉刷新和上拉加载

实现的效果图 系统的 UIRefreshControl 的属性很少,就只有下面几个: isRefreshing: Bool 判断是否正在刷新 tintColor: UIColor! 修改菊花的...
  • understand_XZ
  • understand_XZ
  • 2017-12-06 11:46
  • 222

XZ_iOS之使用KVO实现textField在填写完数据时,button可点击且变颜色

步骤: 1. 注册,指定被观察者的属性, 2. 实现回调方法 3. 移除观察 效果图:当所有的输入框都有值的时候,Button才可以点击           // 用户输入的值在...
  • understand_XZ
  • understand_XZ
  • 2016-07-04 14:18
  • 1294

Swift - 修改顶部状态栏(statusBar)文字颜色为白色

Swift - 修改顶部状态栏(statusBar)文字颜色为白色 默认情况下,APP顶部“运营商”“电量”等状态栏文字颜色是黑色的,但如果界面背景色比较深的话就会不太好...
  • yst19910702
  • yst19910702
  • 2016-10-08 10:29
  • 924

Swift - UIButton应用 1,按钮的创建 (1)按钮有下面四种类型: UIButtonType.system:前面不带图标,默认文字颜色为蓝色,有触摸时的高亮效果 UIButton

Swift - UIButton应用 1,按钮的创建 (1)按钮有下面四种类型: UIButtonType.system:前面不带图标,默认文字颜色为蓝色,有...
  • leemboy
  • leemboy
  • 2017-11-23 22:52
  • 1

iOS8中JS与原生代码交互(SWIFT实现js调用本地代码的2种方法)

  • 2014-08-18 00:41
  • 38KB
  • 下载

(XCode6.1正式版)iOS8中JS与原生代码交互(SWIFT实现js调用本地代码的2种方法)

  • 2014-11-12 08:12
  • 42KB
  • 下载
    个人资料
    • 访问:56179次
    • 积分:1775
    • 等级:
    • 排名:千里之外
    • 原创:119篇
    • 转载:0篇
    • 译文:2篇
    • 评论:18条
    文章分类
    最新评论