diff --git a/Demo/Demo/Debug/Debug.swift b/Demo/Demo/Debug/Debug.swift index 7888dcf..aed0751 100644 --- a/Demo/Demo/Debug/Debug.swift +++ b/Demo/Demo/Debug/Debug.swift @@ -32,6 +32,13 @@ extension Debug { // paragraphs var lineSpacing: CGFloat? var lineHeightMultiple: CGFloat? + var minimumLineHeight: CGFloat? + var maximumLineHeight: CGFloat? + var paragraphSpacing: CGFloat? + var paragraphSpacingBefore: CGFloat? + var firstLineHeadIndent: CGFloat? + var headIndent: CGFloat? + var tailIndent: CGFloat? init() { font = .systemFont(ofSize: 17.0) @@ -50,6 +57,13 @@ extension Debug { case allowsDefaultTighteningForTruncation case lineSpacing case lineHeightMultiple + case minimumLineHeight + case maximumLineHeight + case paragraphSpacing + case paragraphSpacingBefore + case firstLineHeadIndent + case headIndent + case tailIndent } init(from decoder: Decoder) throws { @@ -86,6 +100,20 @@ extension Debug { self.lineSpacing = try container.decodeIfPresent(CGFloat.self, forKey: .lineSpacing) self.lineHeightMultiple = try container.decodeIfPresent(CGFloat.self, forKey: .lineHeightMultiple) + + self.minimumLineHeight = try container.decodeIfPresent(CGFloat.self, forKey: .minimumLineHeight) + + self.maximumLineHeight = try container.decodeIfPresent(CGFloat.self, forKey: .maximumLineHeight) + + self.paragraphSpacing = try container.decodeIfPresent(CGFloat.self, forKey: .paragraphSpacing) + + self.paragraphSpacingBefore = try container.decodeIfPresent(CGFloat.self, forKey: .paragraphSpacingBefore) + + self.firstLineHeadIndent = try container.decodeIfPresent(CGFloat.self, forKey: .firstLineHeadIndent) + + self.headIndent = try container.decodeIfPresent(CGFloat.self, forKey: .headIndent) + + self.tailIndent = try container.decodeIfPresent(CGFloat.self, forKey: .tailIndent) } func encode(to encoder: Encoder) throws { @@ -102,6 +130,13 @@ extension Debug { try container.encodeIfPresent(allowsDefaultTighteningForTruncation, forKey: .allowsDefaultTighteningForTruncation) try container.encodeIfPresent(lineSpacing, forKey: .lineSpacing) try container.encodeIfPresent(lineHeightMultiple, forKey: .lineHeightMultiple) + try container.encodeIfPresent(minimumLineHeight, forKey: .minimumLineHeight) + try container.encodeIfPresent(maximumLineHeight, forKey: .maximumLineHeight) + try container.encodeIfPresent(paragraphSpacing, forKey: .paragraphSpacing) + try container.encodeIfPresent(paragraphSpacingBefore, forKey: .paragraphSpacingBefore) + try container.encodeIfPresent(firstLineHeadIndent, forKey: .firstLineHeadIndent) + try container.encodeIfPresent(headIndent, forKey: .headIndent) + try container.encodeIfPresent(tailIndent, forKey: .tailIndent) } } } diff --git a/Demo/Demo/Debug/DebugLabelView.swift b/Demo/Demo/Debug/DebugLabelView.swift index 7d5d37c..d3a3e9c 100644 --- a/Demo/Demo/Debug/DebugLabelView.swift +++ b/Demo/Demo/Debug/DebugLabelView.swift @@ -126,6 +126,45 @@ class DebugLabelView: UIView { } } + var paragraphSpacing: CGFloat = 0 { + didSet { + let value = paragraphSpacing + paragraphSpacingLabel.text = String(format: "%.2f", value) + paragraphSpacingSlider.value = .init(value) + } + } + + var paragraphSpacingBefore: CGFloat = 0 { + didSet { + let value = paragraphSpacingBefore + paragraphSpacingBeforeLabel.text = String(format: "%.2f", value) + paragraphSpacingBeforeSlider.value = .init(value) + } + } + + var firstLineHeadIndent: CGFloat = 0 { + didSet { + let value = firstLineHeadIndent + firstLineHeadIndentLabel.text = String(format: "%.2f", value) + firstLineHeadIndentSlider.value = .init(value) + } + } + var headIndent: CGFloat = 0 { + didSet { + let value = headIndent + headIndentLabel.text = String(format: "%.2f", value) + headIndentSlider.value = .init(value) + } + } + + var tailIndent: CGFloat = 0 { + didSet { + let value = tailIndent + tailIndentLabel.text = String(format: "%.2f", value) + tailIndentSlider.value = .init(value) + } + } + @IBOutlet private weak var label: UILabel! @IBOutlet private weak var widthLayoutConstraint: NSLayoutConstraint! @@ -252,6 +291,13 @@ extension DebugLabelView { // paragraphs lineSpacing = info.lineSpacing ?? 0 lineHeightMultiple = info.lineHeightMultiple ?? 0 + minimumLineHeight = info.minimumLineHeight ?? 0 + maximumLineHeight = info.maximumLineHeight ?? 0 + paragraphSpacing = info.paragraphSpacing ?? 0 + paragraphSpacingBefore = info.paragraphSpacingBefore ?? 0 + firstLineHeadIndent = info.firstLineHeadIndent ?? 0 + headIndent = info.headIndent ?? 0 + tailIndent = info.tailIndent ?? 0 // 刷新布局 layoutIfNeeded() diff --git a/Demo/Demo/Debug/DebugLabelViewController.swift b/Demo/Demo/Debug/DebugLabelViewController.swift index a697e77..f05ea16 100644 --- a/Demo/Demo/Debug/DebugLabelViewController.swift +++ b/Demo/Demo/Debug/DebugLabelViewController.swift @@ -28,7 +28,7 @@ class DebugLabelViewController: ViewController { super.viewDidLoad() setup() - update() + updateText() } private func setup() { @@ -41,24 +41,75 @@ class DebugLabelViewController: ViewController { } private func set(info: Debug.Label) { + + func update(_ style: AttributedString.Attribute.ParagraphStyle) { + paragraphs.removeAll(where: { $0 ~= style }) + paragraphs.append(style) + } + + func remove(_ style: AttributedString.Attribute.ParagraphStyle) { + paragraphs.removeAll(where: { $0 ~= style }) + } + if let value = info.lineSpacing { - paragraphs.removeAll(where: { $0 == .lineSpacing(0) }) - paragraphs.append(.lineSpacing(value)) + update(.lineSpacing(value)) } else { - paragraphs.removeAll(where: { $0 == .lineSpacing(0) }) + remove(.lineSpacing(0)) } if let value = info.lineHeightMultiple { - paragraphs.removeAll(where: { $0 == .lineHeightMultiple(0) }) - paragraphs.append(.lineHeightMultiple(value)) + update(.lineHeightMultiple(value)) + + } else { + remove(.lineHeightMultiple(0)) + } + if let value = info.minimumLineHeight { + update(.minimumLineHeight(value)) + + } else { + remove(.minimumLineHeight(0)) + } + if let value = info.maximumLineHeight { + update(.maximumLineHeight(value)) + + } else { + remove(.maximumLineHeight(0)) + } + if let value = info.paragraphSpacing { + update(.paragraphSpacing(value)) + + } else { + remove(.paragraphSpacing(0)) + } + if let value = info.paragraphSpacingBefore { + update(.paragraphSpacingBefore(value)) + + } else { + remove(.paragraphSpacingBefore(0)) + } + if let value = info.firstLineHeadIndent { + update(.firstLineHeadIndent(value)) + + } else { + remove(.firstLineHeadIndent(0)) + } + if let value = info.headIndent { + update(.headIndent(value)) + + } else { + remove(.headIndent(0)) + } + if let value = info.tailIndent { + update(.tailIndent(value)) } else { - paragraphs.removeAll(where: { $0 == .lineHeightMultiple(0) }) + remove(.tailIndent(0)) } container.set(info: info) + updateText() } - private func update() { + private func updateText() { container.set(text: .init( attributedString, with: attributes + [.paragraph(paragraphs)] @@ -121,37 +172,32 @@ class DebugLabelViewController: ViewController { } @IBAction func lineSpacingSliderAction(_ sender: UISlider) { - paragraphs.removeAll(where: { $0 == .lineSpacing(0) }) - paragraphs.append(.lineSpacing(.init(sender.value))) info.lineSpacing = .init(sender.value) - update() } @IBAction func lineHeightMultipleSliderAction(_ sender: UISlider) { - paragraphs.removeAll(where: { $0 == .lineHeightMultiple(0) }) - paragraphs.append(.lineHeightMultiple(.init(sender.value))) info.lineHeightMultiple = .init(sender.value) - update() } - @IBAction func minimumLineHeightSliderAction(_ sender: UISlider) { + info.minimumLineHeight = .init(sender.value) } @IBAction func maximumLineHeightSliderAction(_ sender: UISlider) { + info.maximumLineHeight = .init(sender.value) } - @IBAction func paragraphSpacingSliderAction(_ sender: UISlider) { + info.paragraphSpacing = .init(sender.value) } @IBAction func paragraphSpacingBeforeSliderAction(_ sender: UISlider) { + info.paragraphSpacingBefore = .init(sender.value) } - @IBAction func firstLineHeadIndentSliderAction(_ sender: UISlider) { + info.firstLineHeadIndent = .init(sender.value) } - @IBAction func headIndentSliderAction(_ sender: UISlider) { + info.headIndent = .init(sender.value) } - @IBAction func tailIndentSliderAction(_ sender: UISlider) { + info.tailIndent = .init(sender.value) } - } extension DebugLabelViewController: UIScrollViewDelegate { diff --git a/Sources/Extension/UIKit/UILabel/UILabelExtension.swift b/Sources/Extension/UIKit/UILabel/UILabelExtension.swift index 5a62b7b..7c1fd84 100644 --- a/Sources/Extension/UIKit/UILabel/UILabelExtension.swift +++ b/Sources/Extension/UIKit/UILabel/UILabelExtension.swift @@ -270,7 +270,6 @@ fileprivate extension UILabel { guard let attributedString = AttributedString(text) else { return nil } // 构建同步Label的TextKit - // 注: 目前还剩一个截断处理没解决 比如 "a\n\n\nb" numberOfLines=2 let delegate = UILabelLayoutManagerDelegate(scaledMetrics, with: baselineAdjustment) let textStorage = NSTextStorage() let textContainer = NSTextContainer(size: bounds.size) @@ -450,7 +449,7 @@ extension UILabel { ) { (value, range, stop) in guard let old = value as? NSParagraphStyle else { return } guard let new = old.mutableCopy() as? NSMutableParagraphStyle else { return } - new.lineBreakMode = .byWordWrapping + new.lineBreakMode = numberOfLines == 1 ? .byCharWrapping : .byWordWrapping if #available(iOS 11.0, *) { new.setValue(1, forKey: "lineBreakStrategy") } diff --git a/Sources/ParagraphStyle.swift b/Sources/ParagraphStyle.swift index fedc115..ed09772 100644 --- a/Sources/ParagraphStyle.swift +++ b/Sources/ParagraphStyle.swift @@ -157,9 +157,9 @@ extension AttributedString.Attribute.ParagraphStyle { } } -extension AttributedString.Attribute.ParagraphStyle: Equatable { +extension AttributedString.Attribute.ParagraphStyle { - public static func == (lhs: AttributedString.Attribute.ParagraphStyle, rhs: AttributedString.Attribute.ParagraphStyle) -> Bool { + public static func ~= (lhs: AttributedString.Attribute.ParagraphStyle, rhs: AttributedString.Attribute.ParagraphStyle) -> Bool { return lhs.style.keys == rhs.style.keys } }