Swift_富文本绘制以及Url的点击

Swift_富文本绘制以及Url的点击_第1张图片
541AAB10-735B-49DA-8CC9-99E8020662E8.png
TextKit 的核心对象
  • 属性文本存储
    private lazy var textStorage = NSTextStorage()
  • 负责文本字形布局
    1. 绘制背景
    2. 绘制Glyphs 字形
    3. 获取点中字符的索引
    private lazy var layoutManager = NSLayoutManager()
  • 设定文本绘制的范围
    private lazy var textContainer = NSTextContainer()
1 .label的扩展
  • 准备文本系统
  • 使用textStorage接管label内容
private extension AttLabel
{
    ///准备文本系统
    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: ""))
        }
     //遍历范围数组 设置URL 文字的属性
        for r in urlRanges ?? [] {
    textStorage.addAttributes([NSAttributedStringKey.foregroundColor:UIColor.red,
NSAttributedStringKey.backgroundColor:UIColor.blue], range: r)
        }
    }
}
2. 使用正则表达式
  • 返回 textStorge 中的Url的范围数组
private extension AttLabel
{
     var urlRanges:[NSRange]?
    {
     // 1.正则表达式
        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
    }
}
3. 自定义label
  • 重写text属性
  • 准备文本系统
  • 交互 touchesBegan
class AttLabel: UILabel {
   //Mark --`TextKit` 的核心对象 绘制`textStorage`的文本内容
    /// 属性文本存储
    private lazy var textStorage = NSTextStorage()
    /// 负责文本`字形`布局 1.绘制背景  2.绘制Glyphs 字形 3.获取点中字符的索引
    private lazy var layoutManager = NSLayoutManager()
    /// 设定文本绘制的范围
    private lazy var textContainer = NSTextContainer()
    
    // Mark: 一旦内容变化 需要让 textStorge 响应变化
    // Mark: --重写属性
    override var text: String?
        {
        didSet{
            // 重新准备文本内容
            prepareTextSystem()
        }
    }
    // Mark: -- 构造函数
    override init(frame: CGRect) {
        super.init(frame: frame)
      prepareTextSystem()
    }
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      prepareTextSystem()
    }
    // Mark: --交互
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        /// 获取用户点击位置
        guard let location = touches.first?.location(in: self) else{
           return
        }
        /// 获取点中字符的索引
        let index =
        layoutManager.glyphIndex(for: location, in: textContainer)
        /// 判断index是否在ranges的范围内 如果在 就高亮
        for r in urlRanges ?? [] {
         if NSLocationInRange(index,r)
         {
    textStorage.addAttributes([NSAttributedStringKey.foregroundColor:UIColor.white], range: r)
        /// 如果需要重绘 需要调用一个函数 但不是drawRect
         setNeedsDisplay()
         }else
         {
         print("没戳着")
        }
        }
        
        
    }
    override func drawText(in rect: CGRect) {
    
        let range = NSRange(location: 0, length: textStorage.length)
        /// 绘制背景
        layoutManager.drawBackground(forGlyphRange: range, at: CGPoint())
        /// 绘制Glyphs 字形
        /// CGPoint():从原点绘制
        layoutManager.drawGlyphs(forGlyphRange: range, at: CGPoint())
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        //指定绘制文本的区域
        textContainer.size = bounds.size
        
    }
}

你可能感兴趣的:(Swift_富文本绘制以及Url的点击)