From b258608b775b57e2d0073cac7e11382a1ff5a024 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 13 Mar 2024 08:56:08 -0500 Subject: [PATCH] match pattern of the label Signed-off-by: Matt Bruce --- VDS/Components/Buttons/ButtonBase.swift | 130 +++++++++++++++++++----- 1 file changed, 105 insertions(+), 25 deletions(-) diff --git a/VDS/Components/Buttons/ButtonBase.swift b/VDS/Components/Buttons/ButtonBase.swift index cc096d41..121b349b 100644 --- a/VDS/Components/Buttons/ButtonBase.swift +++ b/VDS/Components/Buttons/ButtonBase.swift @@ -121,7 +121,7 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { } open func updateView() { - updateLabel() + restyleText() } open func updateAccessibility() { @@ -152,36 +152,116 @@ open class ButtonBase: UIButton, ViewProtocol, UserInfoable, Clickable { return CGSize(width: adjustedWidth, height: adjustedHeight) } + private enum TextSetMode { + case text + case attributedText + } + + private var textSetMode: TextSetMode = .text + + /// :nodoc: + override public func setTitle(_ title: String?, for state: UIControl.State) { + // When text is set, we may need to re-style it as attributedText + // with the correct paragraph style to achieve the desired line height. + textSetMode = .text + styleText(title, for: state) + } + + /// :nodoc: + override public func setAttributedTitle(_ title: NSAttributedString?, for state: UIControl.State) { + // When text is set, we may need to re-style it as attributedText + // with the correct paragraph style to achieve the desired line height. + textSetMode = .attributedText + styleAttributedText(title, for: state) + } + + /// Gets or sets the text alignment of the button's title label. + /// Default value = `.natural` + public var textAlignment: NSTextAlignment = .natural { + didSet { + if textAlignment != oldValue { + // Text alignment can be part of our paragraph style, so we may need to + // re-style when changed + restyleText() + } + } + } + + /// Gets or sets the line break mode of the button's title label. + /// Default value = `.byTruncatingTail` + public var lineBreakMode: NSLineBreakMode = .byTruncatingTail { + didSet { + if lineBreakMode != oldValue { + // Line break mode can be part of our paragraph style, so we may need to + // re-style when changed + restyleText() + } + } + } + //-------------------------------------------------- // MARK: - Private Methods //-------------------------------------------------- - private func updateLabel() { - - //clear the arrays holding actions - accessibilityCustomActions = [] - if let text, !text.isEmpty { - //create the primary string - let mutableText = NSMutableAttributedString.mutableText(for: text, - textStyle: textStyle, - useScaledFont: useScaledFont, - textColor: textColor, - alignment: titleLabel?.textAlignment ?? .center, - lineBreakMode: titleLabel?.lineBreakMode ?? .byTruncatingTail) - - //apply any attributes - if let attributes = textAttributes { - mutableText.apply(attributes: attributes) - } - - //set the attributed text - setAttributedTitle(mutableText, for: .normal) - setAttributedTitle(mutableText, for: .highlighted) + + private func restyleText() { + if textSetMode == .text { + styleText(text, for: state) } else { - setAttributedTitle(nil, for: .normal) - setAttributedTitle(nil, for: .highlighted) - titleLabel?.text = nil + styleAttributedText(currentAttributedTitle, for: state) } } + + private func styleText(_ newValue: String!, for state: UIControl.State) { + defer { invalidateIntrinsicContentSize() } + guard let newValue else { + // We don't need to use attributed text + super.setAttributedTitle(nil, for: state) + super.setTitle(newValue, for: state) + return + } + + accessibilityCustomActions = [] + + //create the primary string + let mutableText = NSMutableAttributedString.mutableText(for: newValue, + textStyle: textStyle, + useScaledFont: useScaledFont, + textColor: textColor, + alignment: titleLabel?.textAlignment ?? .center, + lineBreakMode: titleLabel?.lineBreakMode ?? .byTruncatingTail) + + //apply any attributes + if let attributes = textAttributes { + mutableText.apply(attributes: attributes) + } + + // Set attributed text to match typography + super.setAttributedTitle(mutableText, for: state) + + } + + private func styleAttributedText(_ newValue: NSAttributedString?, for state: UIControl.State) { + defer { invalidateIntrinsicContentSize() } + guard let newValue = newValue else { + // We don't need any additional styling + super.setAttributedTitle(newValue, for: state) + return + } + + //clear the arrays holding actions + accessibilityCustomActions = [] + + let mutableText = NSMutableAttributedString(attributedString: newValue) + + //apply any attributes + if let attributes = textAttributes { + mutableText.apply(attributes: attributes) + } + + // Modify attributed text to match typography + super.setAttributedTitle(mutableText, for: state) + } + }