<commit_before>//
//  TLLabel.swift
//  007-TextKit
//
//  Created by lichuanjun on 2017/8/6.
//  Copyright © 2017年 lichuanjun. All rights reserved.
//

import UIKit

/**
 1. 使用 TextKit 接管 Label 的底层实现 - 绘制 textStorage 的文本内容
 2. 使用正则表达式过滤 URL，设置 URL 的特殊显示
 3. 交互
 
 - UILabel 默认不能实现垂直顶部对齐，使用 TextKit 可以
 */
class TLLabel: UILabel {

    // MARK: - 构造函数
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        prepareTextSystem()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        prepareTextSystem()
    }
    
    /// 绘制文本
    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
    }
    
    // MARK: - TextKit 的核心对象
    
    /// 属性文本存储
    fileprivate lazy var textStorage = NSTextStorage()
    /// 负责文本`字形`布局
    fileprivate lazy var layoutManager = NSLayoutManager()
    /// 设定文本绘制的范围
    fileprivate lazy var  textContainer = NSTextContainer()
}

// MARK: - 设置 TextKit 核心对象
private extension TLLabel {
    
    // 准备文本系统
    func prepareTextSystem() {
        // 1. 准备文本内容
        prepareTextContent()
        
        // 2. 设置对象的关系
        textStorage.addLayoutManager(layoutManager)
        layoutManager.addTextContainer(textContainer)
    }
    
    /// 准备文本内容 - 使用 textStorage 结果 label 的内容<commit_msg>和 URL 的文本交互，高亮显示(setNeedsDisplay)<commit_after>//
//  TLLabel.swift
//  007-TextKit
//
//  Created by lichuanjun on 2017/8/6.
//  Copyright © 2017年 lichuanjun. All rights reserved.
//

import UIKit

/**
 1. 使用 TextKit 接管 Label 的底层实现 - 绘制 textStorage 的文本内容
 2. 使用正则表达式过滤 URL，设置 URL 的特殊显示
 3. 交互
 
 - UILabel 默认不能实现垂直顶部对齐，使用 TextKit 可以
 */
class TLLabel: UILabel {

    // 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<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 r in urlRanges ?? [] {
            // NSRange 跳入文件可见此方法
            if NSLocationInRange(idx, r) {
                print("需要高亮")
                
                textStorage.addAttributes([NSForegroundColorAttributeName: UIColor.blue], range: r)
                
                // 如果需要重绘，需要调用 setNeedsDisplay 函数，但是不是 drawRect
                setNeedsDisplay()
            } else {
                print("没戳着")
            }
        }
    }
    
    /// 绘制文本
    /**
     - 在 iOS 中绘制工作时类似于`油画`似的，后绘制的内容，会把之前绘制的内容覆盖
     - 尽量避免使用带透明度的颜色，会严重影响性能
     */
    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
    }
    
    // MARK: - TextKit 的核心对象
    
    /// 属性文本存储
    fileprivate lazy var textStorage = NSTextStorage()
    /// 负责文本`字形`布局
    fileprivate lazy var layoutManager = NSLayoutManager()
    /// 设定文本绘制的范围
    fileprivate lazy var  textContainer = NSTextContainer()
}

// MARK: - 设置 TextKit 核心对象
private extension TLLabel {
    
    // 准备文本系统
    func prepareTextSystem() {
        
        // 0. 开启用户交互
        isUserInteractionEnabled = true
        
        // 1. 准备文本内容
        prepareTextContent()
        
        // 2. 设置对象的关系
        textStorage.addLayoutManager(layoutManager)
        layoutManager.addTextContainer(textContainer)
    }
    
    /// 准备文本内容 - 使用 textStorage 结果 label 的内容
{'fraction_non_alphanumeric': 0.09810055865921788, 'fraction_numerical': 0.009385474860335195, 'mean_word_length': 1.806269592476489, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4315994', 'n_tokens_mistral': 1735, 'n_tokens_neox': 1794, 'n_words': 327}