Skip to content

Commit 589ce1e

Browse files
committed
使用Neat的方案 尝试解决TextKit创建与UILabel一致的内容, 已解决了Neat无法正确处理带有NSTextAttachment的内容的问题, 但是目前还是失败了, 依旧无法正确推测出UILabel内部具体如何进行的行高计算.
1 parent 06ad7ec commit 589ce1e

4 files changed

Lines changed: 76 additions & 34 deletions

File tree

Demo/Demo/Details/CheckingViewController.swift

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class CheckingViewController: ViewController<CheckingView> {
1313

1414
override func viewDidLoad() {
1515
super.viewDidLoad()
16+
1617
// 添加电话号码类型监听
1718
container.label.attributed.observe([.phoneNumber], highlights: [.foreground(#colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1))]) { (result) in
1819
print(result)
@@ -35,27 +36,50 @@ class CheckingViewController: ViewController<CheckingView> {
3536
}
3637

3738
do {
38-
var string: AttributedString = """
39-
我的名字叫李响,我的手机号码是18611401994,我的电子邮件地址是18611401994@163.com,现在是2020/06/28 20:30。我的GitHub主页是https://github.com/lixiang1994。欢迎来Star! \("点击联系我", .action(clicked))
40-
"""
41-
string.add(attributes: [.foreground(#colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)), .font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.phoneNumber])
42-
string.add(attributes: [.foreground(#colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)), .font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.link])
43-
string.add(attributes: [.foreground(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)), .font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.date])
44-
string.add(attributes: [.font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.action])
45-
container.label.attributed.text = string
39+
40+
// 目前的问题是, 如果内容显示全 是OK的, 如果显示不全 就会有问题, 比如numberOfLines = x 或者 height较小.
41+
42+
let label = UILabel(frame: .init(x: 15, y: 80, width: 414 - 30, height: 750))
43+
label.backgroundColor = .white
44+
view.addSubview(label)
45+
label.attributed.observe(.regex("a")) { (result) in
46+
// 随便添加个监听 以便触发点击事件 显示DebugView
47+
}
48+
// label.font = UIFont(name: "Georgia", size: 20)!
49+
label.font = .systemFont(ofSize: 20)
50+
label.numberOfLines = 0 // 限制行数后会存在显示不一致的问题 目前无法解决
51+
label.lineBreakMode = .byTruncatingTail
52+
53+
let string: AttributedString = .init(
54+
"""
55+
\("iCloud 🤗能将你的 GarageBand 创作进度在你所有的 iOS 设备间保持更新🤗。", .font(UIFont(name: "Georgia-Italic", size: 30)!), .paragraph(.lineSpacing(10)))\n它还可以让你在 iPad、iPhone 或 iPod\(.image(#imageLiteral(resourceName: "huaji"), .custom(.center, size: .init(width: 133, height: 133)))) touch 上开始勾勒(灬ꈍ ꈍ灬)一首歌的灵感,然后用 iCloud Drive 将音轨导入 Mac 做进一步创作,再将完成的作品共享到你的任何设备。你还可以导入 Logic Pro 项目的便携版本,接着创作其他音轨。\n\n当你重新在 \("Logic Pro", .font(UIFont(name: "HelveticaNeue", size: 30)!)) 打开该项目时,所😺有原始音轨以及在 GarageBand 中另外添加的音轨,都将🥔同时显示出来。Hello world\(.image(#imageLiteral(resourceName: "swift-icon"), .proposed()))
56+
""", .paragraph(.firstLineHeadIndent(10), .paragraphSpacing(5))
57+
)
58+
label.attributed.text = string
4659
}
4760

48-
do {
49-
var string: AttributedString = """
50-
My name is Li Xiang, my mobile phone number is 18611401994, my email address is 18611401994@163.com, I live in No.10 Xitucheng Road, Haidian District, Beijing, China, and it is now 20:30 on June 28, 2020. My GitHub homepage is https://github.com/lixiang1994. Welcome to star me! \("Contact me", .action(clicked))
51-
"""
52-
string.add(attributes: [.foreground(#colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1))], checkings: [.address])
53-
string.add(attributes: [.foreground(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1))], checkings: [.link, .phoneNumber])
54-
string.add(attributes: [.foreground(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1))], checkings: [.date])
55-
string.add(attributes: [.foreground(#colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1))], checkings: [.regex("Li Xiang")])
56-
string.add(attributes: [.font(.systemFont(ofSize: 16, weight: .medium))], checkings: [.action])
57-
container.textView.attributed.text = string
58-
}
61+
// do {
62+
// var string: AttributedString = """
63+
// 我的名字叫李响,我的手机号码是18611401994,我的电子邮件地址是18611401994@163.com,现在是2020/06/28 20:30。我的GitHub主页是https://github.com/lixiang1994。欢迎来Star! \("点击联系我", .action(clicked))
64+
// """
65+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)), .font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.phoneNumber])
66+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)), .font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.link])
67+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)), .font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.date])
68+
// string.add(attributes: [.font(.systemFont(ofSize: 20, weight: .medium))], checkings: [.action])
69+
// container.label.attributed.text = string
70+
// }
71+
72+
// do {
73+
// var string: AttributedString = """
74+
// My name is Li Xiang, my mobile phone number is 18611401994, my email address is 18611401994@163.com, I live in No.10 Xitucheng Road, Haidian District, Beijing, China, and it is now 20:30 on June 28, 2020. My GitHub homepage is https://github.com/lixiang1994. Welcome to star me! \("Contact me", .action(clicked))
75+
// """
76+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1))], checkings: [.address])
77+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1))], checkings: [.link, .phoneNumber])
78+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1))], checkings: [.date])
79+
// string.add(attributes: [.foreground(#colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1))], checkings: [.regex("Li Xiang")])
80+
// string.add(attributes: [.font(.systemFont(ofSize: 16, weight: .medium))], checkings: [.action])
81+
// container.textView.attributed.text = string
82+
// }
5983

6084
container.tintAdjustmentMode = .normal
6185
}

Sources/Extension/UIKit/UILabel/UIFontExtension.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ extension UIFont {
6363
var a_xHeight: CGFloat {
6464
systemFont.a_xHeight
6565
}
66+
67+
var debug: String {
68+
return """
69+
familyName: \(familyName)
70+
lineHeight: \(lineHeight)
71+
descender: \(descender)
72+
leading: \(leading)
73+
ascender: \(ascender)
74+
capHeight: \(capHeight)
75+
xHeight: \(xHeight)
76+
"""
77+
}
6678
}
6779

6880
#endif

Sources/Extension/UIKit/UILabel/UILabelExtension.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extension AttributedStringWrapper where Base: UILabel {
3131
public var text: AttributedString? {
3232
get { base.touched?.0 ?? AttributedString(base.attributedText) }
3333
set {
34+
// 字体补丁 交互所有字体的属性 更换为系统字体的值
3435
UIFont.Patch
3536
// 判断当前是否在触摸状态, 内容是否发生了变化
3637
if var touched = base.touched, touched.0.isContentEqual(to: newValue) {
@@ -309,6 +310,9 @@ private extension UILabel {
309310
guard let old = value as? NSParagraphStyle else { return }
310311
guard let new = old.mutableCopy() as? NSMutableParagraphStyle else { return }
311312
new.lineBreakMode = .byWordWrapping
313+
if #available(iOS 11.0, *) {
314+
new.setValue(1, forKey: "lineBreakStrategy")
315+
}
312316
mutable.addAttribute(.paragraphStyle, value: new, range: range)
313317
}
314318
return mutable
@@ -330,15 +334,22 @@ fileprivate extension UILabel {
330334
let text = adaptation(scaledAttributedText ?? synthesizedAttributedText ?? attributedText)
331335
guard let attributedString = AttributedString(text) else { return nil }
332336

337+
struct BaseLineInfo {
338+
let firstBaseline: Double
339+
let lastBaseline: Double
340+
let referenceBounds: CGRect
341+
let measuredNumberOfLines: Int64
342+
}
343+
333344
// 构建同步Label设置的TextKit
334345
let textStorage = NSTextStorage(attributedString: attributedString.value)
335-
let textContainer = NSTextContainer(size: bounds.size)
346+
let textContainer = NSTextContainer(size: .init(bounds.size.width, bounds.size.height + 1))
336347
let layoutManager = NSLayoutManager()
337-
layoutManager.delegate = UILabelLayoutManagerDelegate.shared // 重新计算行高确保TextKit与UILabel显示一致
348+
layoutManager.delegate = UILabelLayoutManagerDelegate.shared // 重新计算行高确保TextKit与UILabel显示一致
338349
textContainer.lineBreakMode = lineBreakMode
339350
textContainer.lineFragmentPadding = 0.0
340351
textContainer.maximumNumberOfLines = numberOfLines
341-
layoutManager.usesFontLeading = false // 不使用字体的头 因为非系统字体会出现问题
352+
layoutManager.usesFontLeading = false
342353
layoutManager.addTextContainer(textContainer)
343354
textStorage.addLayoutManager(layoutManager)
344355

@@ -353,10 +364,10 @@ fileprivate extension UILabel {
353364
point.y -= (bounds.height - height) / 2
354365

355366
// Debug
356-
// subviews.filter({ $0 is DebugView }).forEach({ $0.removeFromSuperview() })
357-
// let view = DebugView(frame: .init(x: 0, y: (bounds.height - height) / 2, width: bounds.width, height: height))
358-
// view.draw = { layoutManager.drawGlyphs(forGlyphRange: .init(location: 0, length: textStorage.length), at: .zero) }
359-
// addSubview(view)
367+
subviews.filter({ $0 is DebugView }).forEach({ $0.removeFromSuperview() })
368+
let view = DebugView(frame: .init(x: 0, y: (bounds.height - height) / 2, width: bounds.width, height: height))
369+
view.draw = { layoutManager.drawGlyphs(forGlyphRange: .init(location: 0, length: textStorage.length), at: .zero) }
370+
addSubview(view)
360371

361372
// 获取字形下标
362373
var fraction: CGFloat = 0

Sources/Extension/UIKit/UILabel/UILabelLayoutManagerDelegate.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ class UILabelLayoutManagerDelegate: NSObject, NSLayoutManagerDelegate {
3131
}
3232
// 获取当前所有属性
3333
let attributes = getAttributes(layoutManager, with: textStorage, for: glyphRange)
34+
// 如果有附件 直接跳过 可以解决附件导致的计算错误
3435
guard !attributes.contains(where: { $0.attributes[.attachment] != nil }) else {
3536
return false
3637
}
37-
3838
// 获取行高最大的属性
3939
guard
4040
let item = getMaxAttributes(attributes),
@@ -50,12 +50,7 @@ class UILabelLayoutManagerDelegate: NSObject, NSLayoutManagerDelegate {
5050
var baseline = lineHeight + defaultFont.descender
5151
rect.size.height = lineHeight
5252

53-
// 某些字体 (比如 emoji) 的行高会比计算的大.
54-
// 如果设置 UsedRect 为一行的高度, 最后一行就会消失.
55-
// 所以只用比原有 UsedRect 高度更大的行高
56-
// 这可能导致 UITextView 在最后一行下面有多余的填充, 可以通过将 maxLineHeight 设置为 getLineHeight(:, with:) 计算的行高.
57-
used.size.height = max(lineHeight, used.height)
58-
53+
used.size.height = lineHeight
5954
/**
6055
From apple's doc:
6156
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html
@@ -144,7 +139,7 @@ extension UILabelLayoutManagerDelegate {
144139
glyphRange = NSRange(location: glyphRange.location, length: glyphRange.length - 1)
145140
}
146141
}
147-
142+
// 循环遍历获取当前字形范围内的所有属性
148143
let targetRange = layoutManager.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
149144
var array: [(NSRange, [NSAttributedString.Key: Any])] = []
150145
var lastIndex = -1

0 commit comments

Comments
 (0)