UIButton 的 attributeTitle 的动态类型
Dynamic type for UIButton's attributeTitle
有人在 UIButton
上为 attributedTitle
使用过动态类型吗?考虑下面的超级简单代码:
let font = UIFont(name: "Helvetica", size: 14)!
let scaledFont = UIFontMetrics.default.scaledFont(for: font)
let button = UIButton(type: .custom)
button.titleLabel?.font = scaledFont
button.titleLabel?.adjustsFontForContentSizeCategory = true
let attributes: [NSAttributedString.Key: Any] = [ .font: scaledFont ]
let attributedText = NSAttributedString(string: "Press me", attributes: attributes)
button.setAttributedTitle(attributedText, for: .normal)
如果我使用辅助功能检查器放大和缩小字体大小,按钮的大小和标签文本无法正确缩放。
不过,如果我只是调用 button.setTitle()
并传递一个普通字符串,动态类型缩放就可以正常工作。
直接在 UILabel
上对属性文本使用相同的模式效果很好……当我对 UIButton
的标题使用属性文本时似乎是这样。
任何想法或建议都会很棒。谢谢
编辑: 经过更多的探索,看起来发生的事情是文本试图缩放,但按钮的 width/height 没有增长用它。如果我将动态类型拨到最大文本大小,然后创建屏幕并继续缩小字体大小,它工作正常,因为按钮 width/height 约束设置为初始较大的值。但是,如果我从一个小的动态类型设置开始,然后变大,按钮将无法适应文本大小的变化
If I scale the font size up and down using Accessibility Inspector, the button's size and label text doesn’t scale properly.
要将 UIButton
的属性字符串标签缩放到 Dynamic Type
,首先将标题标签设置为属性字符串,然后放置此元素在 setAttributedTitle
按钮方法中。
关于按钮大小,在UITraitEnvironment
协议的traitCollectionDidChange实例方法中指定按钮的sizeToFit
方法(使用约束也可以是另一种解决方案).
Copy-paste 下面的代码片段 (Swift 5.0, iOS 12):
class ViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.layer.borderColor = UIColor.black.cgColor
myButton.layer.borderWidth = 4.0
myButton.contentEdgeInsets = UIEdgeInsets(top: 10,
left: 20,
bottom: 10,
right: 20)
let font = UIFont(name: "Helvetica", size: 19)!
let scaledFont = UIFontMetrics.default.scaledFont(for: font)
let attributes = [NSAttributedString.Key.font: scaledFont]
let attributedText = NSAttributedString(string: "Press me",
attributes: attributes)
myButton.titleLabel?.attributedText = attributedText
myButton.setAttributedTitle(myButton.titleLabel?.attributedText,
for: .normal)
myButton.titleLabel?.adjustsFontForContentSizeCategory = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
myButton.sizeToFit()
}
}
... 之后你会得到结果:
如果您需要进一步的解释,我建议您看一下这个 Dynamic Type kind of tutorial that contains {code snippets + illustrations} and at this WWDC detailed summary,它涉及使用动态类型构建应用程序。
⚠️ 编辑 2021/02/15 ⚠️
感谢@Anthony 的评论,我已针对新上下文改进了这个旧解决方案,如下所示:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.layer.borderColor = UIColor.black.cgColor
myButton.layer.borderWidth = 4.0
let font = UIFont(name: "Helvetica", size: 20)!
let scaledFont = UIFontMetrics.default.scaledFont(for: font)
let attributes = [NSAttributedString.Key.font: scaledFont]
let attributedText = NSAttributedString(string: "Press Me Huge Button",
attributes: attributes)
myButton.titleLabel?.attributedText = attributedText
myButton.titleLabel?.numberOfLines = 0
myButton.titleLabel?.textAlignment = .center
myButton.setAttributedTitle(attributedText,
for: .normal)
myButton.titleLabel?.adjustsFontForContentSizeCategory = true
createConstraints()
}
...并在按钮及其内容之间添加约束:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
DispatchQueue.main.async() {
self.myButton.setNeedsUpdateConstraints() // For updating constraints.
}
}
private func createConstraints() {
myButton.translatesAutoresizingMaskIntoConstraints = false
myButton.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
let spacing = 10.0
myButton.titleLabel?.trailingAnchor.constraint(equalTo: myButton.trailingAnchor,
constant: -CGFloat(spacing)).isActive = true
myButton.titleLabel?.leadingAnchor.constraint(equalTo: myButton.leadingAnchor,
constant: CGFloat(spacing)).isActive = true
myButton.titleLabel?.topAnchor.constraint(equalTo: myButton.topAnchor,
constant: CGFloat(spacing)).isActive = true
myButton.titleLabel?.bottomAnchor.constraint(equalTo: myButton.bottomAnchor,
constant: -CGFloat(spacing)).isActive = true
}
Interface Builder
中的按钮有了一些新的约束,我终于使用 Xcode
Environment Overrides pane 获得了这些屏幕截图:
⚠️ 编辑 2022/01/28 ⚠️
iOS 15 介绍了new button style that provides a native adaptation to the multiline titles and the Dynamic Type的特点。
无需任何代码即可实现 Craig 的初始目标。
以防万一有人跟进,解决这个问题的关键包含在@XLE_22的post的第一段中,但非常微妙。
在按钮上调用 setAttributedTitle()
时,您需要从 标签 传递属性字符串。 IE。 myButton.titleLabel?.attributedText
... 传递局部变量 attributedText
不起作用。我无法解释为什么会这样。考虑以下代码:
myButton.titleLabel?.attributedText = attributedText
let labelAttributedText = myButton.titleLabel?.attributedText
myButton.setAttributedTitle(labelAttributedText, for: .normal) // works
myButton.setAttributedTitle(attributedText, for: .normal) // doesn't work
如果我比较attributedText
和labelAttributedText
,我可以看出前者是NSConcreteAttributedString
而后者是NSConcreteMutableAttributedString
。问他们是否相等 return true
并且快速比较它们的属性似乎表明它们确实相同,但必须有一些细微的不同。
我最初尝试将 attributedText
创建为可变属性字符串,但这也无济于事。非常令人费解,但非常感谢@XLE_22 的提示......我怀疑我自己是否会尝试过这个特定的技巧。
在 Swift 5.5 & XCode 13
如果您使用的是自定义字体。也许这段代码会对你有所帮助。
class DynamicSizeButton: UIButton {
func customStyle() {
guard let currentFont = self.titleLabel?.font, let customFont = UIFont(name: currentFont.fontName, size: currentFont.pointSize) else {return}
self.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont)
self.titleLabel?.adjustsFontForContentSizeCategory = true
}
override func awakeFromNib() {
super.awakeFromNib()
customStyle()
}
}
使用.......
有人在 UIButton
上为 attributedTitle
使用过动态类型吗?考虑下面的超级简单代码:
let font = UIFont(name: "Helvetica", size: 14)!
let scaledFont = UIFontMetrics.default.scaledFont(for: font)
let button = UIButton(type: .custom)
button.titleLabel?.font = scaledFont
button.titleLabel?.adjustsFontForContentSizeCategory = true
let attributes: [NSAttributedString.Key: Any] = [ .font: scaledFont ]
let attributedText = NSAttributedString(string: "Press me", attributes: attributes)
button.setAttributedTitle(attributedText, for: .normal)
如果我使用辅助功能检查器放大和缩小字体大小,按钮的大小和标签文本无法正确缩放。
不过,如果我只是调用 button.setTitle()
并传递一个普通字符串,动态类型缩放就可以正常工作。
直接在 UILabel
上对属性文本使用相同的模式效果很好……当我对 UIButton
的标题使用属性文本时似乎是这样。
任何想法或建议都会很棒。谢谢
编辑: 经过更多的探索,看起来发生的事情是文本试图缩放,但按钮的 width/height 没有增长用它。如果我将动态类型拨到最大文本大小,然后创建屏幕并继续缩小字体大小,它工作正常,因为按钮 width/height 约束设置为初始较大的值。但是,如果我从一个小的动态类型设置开始,然后变大,按钮将无法适应文本大小的变化
If I scale the font size up and down using Accessibility Inspector, the button's size and label text doesn’t scale properly.
要将 UIButton
的属性字符串标签缩放到 Dynamic Type
,首先将标题标签设置为属性字符串,然后放置此元素在 setAttributedTitle
按钮方法中。
关于按钮大小,在UITraitEnvironment
协议的traitCollectionDidChange实例方法中指定按钮的sizeToFit
方法(使用约束也可以是另一种解决方案).
Copy-paste 下面的代码片段 (Swift 5.0, iOS 12):
class ViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.layer.borderColor = UIColor.black.cgColor
myButton.layer.borderWidth = 4.0
myButton.contentEdgeInsets = UIEdgeInsets(top: 10,
left: 20,
bottom: 10,
right: 20)
let font = UIFont(name: "Helvetica", size: 19)!
let scaledFont = UIFontMetrics.default.scaledFont(for: font)
let attributes = [NSAttributedString.Key.font: scaledFont]
let attributedText = NSAttributedString(string: "Press me",
attributes: attributes)
myButton.titleLabel?.attributedText = attributedText
myButton.setAttributedTitle(myButton.titleLabel?.attributedText,
for: .normal)
myButton.titleLabel?.adjustsFontForContentSizeCategory = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
myButton.sizeToFit()
}
}
... 之后你会得到结果:
⚠️ 编辑 2021/02/15 ⚠️
感谢@Anthony 的评论,我已针对新上下文改进了这个旧解决方案,如下所示:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.layer.borderColor = UIColor.black.cgColor
myButton.layer.borderWidth = 4.0
let font = UIFont(name: "Helvetica", size: 20)!
let scaledFont = UIFontMetrics.default.scaledFont(for: font)
let attributes = [NSAttributedString.Key.font: scaledFont]
let attributedText = NSAttributedString(string: "Press Me Huge Button",
attributes: attributes)
myButton.titleLabel?.attributedText = attributedText
myButton.titleLabel?.numberOfLines = 0
myButton.titleLabel?.textAlignment = .center
myButton.setAttributedTitle(attributedText,
for: .normal)
myButton.titleLabel?.adjustsFontForContentSizeCategory = true
createConstraints()
}
...并在按钮及其内容之间添加约束:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
DispatchQueue.main.async() {
self.myButton.setNeedsUpdateConstraints() // For updating constraints.
}
}
private func createConstraints() {
myButton.translatesAutoresizingMaskIntoConstraints = false
myButton.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
let spacing = 10.0
myButton.titleLabel?.trailingAnchor.constraint(equalTo: myButton.trailingAnchor,
constant: -CGFloat(spacing)).isActive = true
myButton.titleLabel?.leadingAnchor.constraint(equalTo: myButton.leadingAnchor,
constant: CGFloat(spacing)).isActive = true
myButton.titleLabel?.topAnchor.constraint(equalTo: myButton.topAnchor,
constant: CGFloat(spacing)).isActive = true
myButton.titleLabel?.bottomAnchor.constraint(equalTo: myButton.bottomAnchor,
constant: -CGFloat(spacing)).isActive = true
}
Interface Builder
中的按钮有了一些新的约束,我终于使用 Xcode
Environment Overrides pane 获得了这些屏幕截图:
⚠️ 编辑 2022/01/28 ⚠️
iOS 15 介绍了new button style that provides a native adaptation to the multiline titles and the Dynamic Type的特点。
无需任何代码即可实现 Craig 的初始目标。
以防万一有人跟进,解决这个问题的关键包含在@XLE_22的post的第一段中,但非常微妙。
在按钮上调用 setAttributedTitle()
时,您需要从 标签 传递属性字符串。 IE。 myButton.titleLabel?.attributedText
... 传递局部变量 attributedText
不起作用。我无法解释为什么会这样。考虑以下代码:
myButton.titleLabel?.attributedText = attributedText
let labelAttributedText = myButton.titleLabel?.attributedText
myButton.setAttributedTitle(labelAttributedText, for: .normal) // works
myButton.setAttributedTitle(attributedText, for: .normal) // doesn't work
如果我比较attributedText
和labelAttributedText
,我可以看出前者是NSConcreteAttributedString
而后者是NSConcreteMutableAttributedString
。问他们是否相等 return true
并且快速比较它们的属性似乎表明它们确实相同,但必须有一些细微的不同。
我最初尝试将 attributedText
创建为可变属性字符串,但这也无济于事。非常令人费解,但非常感谢@XLE_22 的提示......我怀疑我自己是否会尝试过这个特定的技巧。
在 Swift 5.5 & XCode 13
如果您使用的是自定义字体。也许这段代码会对你有所帮助。
class DynamicSizeButton: UIButton {
func customStyle() {
guard let currentFont = self.titleLabel?.font, let customFont = UIFont(name: currentFont.fontName, size: currentFont.pointSize) else {return}
self.titleLabel?.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont)
self.titleLabel?.adjustsFontForContentSizeCategory = true
}
override func awakeFromNib() {
super.awakeFromNib()
customStyle()
}
}
使用.......