Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 48 additions & 5 deletions Sources/Extension/UIKit/UILabel/UILabelExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,14 @@ fileprivate extension UILabel {
}

func matching(_ point: CGPoint) -> (NSRange, Action)? {
let text = adaptation(scaledAttributedText ?? synthesizedAttributedText ?? attributedText, with: numberOfLines)
let text = adaptation(
scaledAttributedText ?? synthesizedAttributedText ?? attributedText,
with: numberOfLines,
lineBreakMode: lineBreakMode,
textAlignment: textAlignment
)
guard let attributedString = AttributedString(text) else { return nil }
guard attributedString.value.length > 0 else { return nil }

// 构建同步Label的TextKit
let delegate = UILabelLayoutManagerDelegate(scaledMetrics, with: baselineAdjustment)
Expand Down Expand Up @@ -302,6 +308,16 @@ fileprivate extension UILabel {
// 获取字形下标
var fraction: CGFloat = 0
let glyphIndex = layoutManager.glyphIndex(for: point, in: textContainer, fractionOfDistanceThroughGlyph: &fraction)
let glyphRange = layoutManager.glyphRange(for: textContainer)
guard glyphRange.contains(glyphIndex) else {
return nil
}
// A truncated line can still map a touch to a character that is not
// visible. Do not trigger an action for the hidden part of the text.
let truncatedGlyphRange = layoutManager.truncatedGlyphRange(inLineFragmentForGlyphAt: glyphIndex)
guard truncatedGlyphRange.location == NSNotFound || !truncatedGlyphRange.contains(glyphIndex) else {
return nil
}
// 获取字符下标
let index = layoutManager.characterIndexForGlyph(at: glyphIndex)
// 通过字形距离判断是否在字形范围内
Expand Down Expand Up @@ -431,7 +447,12 @@ extension UILabel {
return scaledMetrics?.scaledAttributedText
}

private func adaptation(_ string: NSAttributedString?, with numberOfLines: Int) -> NSAttributedString? {
private func adaptation(
_ string: NSAttributedString?,
with numberOfLines: Int,
lineBreakMode: NSLineBreakMode,
textAlignment: NSTextAlignment
) -> NSAttributedString? {
/**
由于富文本中的lineBreakMode对于UILabel和TextKit的行为是不一致的, UILabel默认的.byTruncatingTail在TextKit中则无法正确显示.
所以将富文本中的lineBreakMode全部替换为TextKit默认的.byWordWrapping, 以解决多行显示和不一致的问题.
Expand All @@ -442,14 +463,36 @@ extension UILabel {
}

let mutable = NSMutableAttributedString(attributedString: string)
let wrappingMode: NSLineBreakMode
if numberOfLines == 1 {
wrappingMode = .byCharWrapping
} else {
switch lineBreakMode {
case .byTruncatingHead, .byTruncatingMiddle, .byTruncatingTail:
// TextKit performs the final truncation in the text
// container. The paragraph itself must remain wrappable.
wrappingMode = .byWordWrapping
default:
wrappingMode = lineBreakMode
}
}
mutable.enumerateAttribute(
.paragraphStyle,
in: .init(location: 0, length: mutable.length),
options: .longestEffectiveRangeNotRequired
) { (value, range, stop) in
guard let old = value as? NSParagraphStyle else { return }
guard let new = old.mutableCopy() as? NSMutableParagraphStyle else { return }
new.lineBreakMode = numberOfLines == 1 ? .byCharWrapping : .byWordWrapping
let new: NSMutableParagraphStyle
if let old = value as? NSParagraphStyle,
let copy = old.mutableCopy() as? NSMutableParagraphStyle {
new = copy
} else {
new = NSMutableParagraphStyle()
new.alignment = textAlignment
}
if new.alignment == .natural {
new.alignment = textAlignment
}
new.lineBreakMode = wrappingMode
if #available(iOS 11.0, *) {
new.setValue(1, forKey: "lineBreakStrategy")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class UILabelLayoutManagerDelegate: NSObject, NSLayoutManagerDelegate {
used.size.height = scaledMetrics.scaledSize.height

case .alignCenters:
print(scaledMetrics)
// 居中的基线偏移 使用Scaled的尺寸高度
var baseline = baselineOffset.pointee
// 整行的占用高度 - 缩放的行高 = 上下边距; 上边距 = 上下边距 * 0.5; 居中的基线偏移 = 上边距 + 缩放的基线偏移
Expand Down Expand Up @@ -111,13 +110,10 @@ class UILabelLayoutManagerDelegate: NSObject, NSLayoutManagerDelegate {
lineFragmentRect.pointee = rect
lineFragmentUsedRect.pointee = used

/**
From apple's doc:
true if you modified the layout information and want your modifications to be used or false if the original layout information should be used.
But actually returning false is also used. : )
We should do this to solve the problem of exclusionPaths not working.
*/
return false
// The rectangles above are the synchronization layer between UILabel
// and TextKit. Returning false tells NSLayoutManager to discard those
// changes and use its proposed geometry instead.
return true
}

// Implementing this method with a return value 0 will solve the problem of last line disappearing
Expand Down