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. 使用正则表达式
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<UITouch>, 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
}
}