swift 只更改AttributedString的字符串(例如UIButton配置attributedTitle)

0lvr5msh  于 2023-08-02  发布在  Swift
关注(0)|答案(1)|浏览(99)

从iOS 15开始,Swift提供了AttributedString结构体,该结构体包含字符串的文本沿着样式属性。问题:给定一个现有的AttributedString,并且假设(为了简单起见)属性由一个样式运行组成,你如何更改 * 只是字符串的部分 * AttributedString?
下面是一个典型的用例-UIButton。假设我有一个基于配置的按钮,带有属性标题:

let button = UIButton(configuration: .plain())
let font = UIFont(name: "Georgia", size: 16)
button.configuration?.attributedTitle = AttributedString(
    "Hello", attributes: AttributeContainer.font(font!)
)

字符串
如果我稍后将配置的标题设置为不同的标题,则属性信息将丢失。举例来说:

button.configuration?.title = "Goodbye"
// Button title is no longer in Georgia font!


显然,我在这里要做的是替换属性字符串标题的 text,而不干扰它的 attributes。但是Swift的AttributedString似乎并没有提供这样的方法。
所以,正如我一开始就问的:正确的方法是什么?

ktecyv1j

ktecyv1j1#

更改AttributedString的文本非常棘手。您必须替换属性化字符串的字符视图的内容-它的characters属性。更难的是,你不能简单地通过分配另一个字符串来实现这一点!
以我们的按钮为例,这不会编译:

button.configuration?.attributedTitle?.characters = "Goodbye" // error

字符串
从简单的字符串派生字符视图也不够。这也不会编译:

button.configuration?.attributedTitle?.characters = "Goodbye".characters // error


这是因为简单字符串的单独字符视图不再存在;你还在尝试将字符串赋值到字符视图中,我们已经知道你不能这样做。
相反,您可以直接从String创建AttributedString.CharacterView,并将 that 赋值给目标属性字符串的characters属性。Swift推断类型在这里有很大的帮助:

button.configuration?.attributedTitle?.characters = .init("Goodbye")


这将替换按钮的标题,而不会干扰按钮标题的样式属性。

按钮详情

替换按钮的标题是一件非常有用的事情,所以我在UIButton上做了一个小的实用扩展,涵盖了所有的情况-一个按钮不是基于配置的,一个按钮是基于配置的但没有属性标题,一个按钮是基于配置的但有属性标题:

extension UIButton {
    func replaceTitle(_ newTitle: String) {
        guard configuration != nil else {
            setTitle(newTitle, for: .normal)
            return
        }
        guard configuration?.attributedTitle != nil else {
            configuration?.title = newTitle
            return
        }
        configuration?.attributedTitle?.characters = .init(newTitle)
    }
}

按钮更多

在基于配置的按钮的情况下,人们可能会问到,为什么要使用attributedTitle?为什么不在按钮配置的buttonConfig.titleTextAttributesTransformer中设置字体?
答案是,对于需要动态响应用户更改其文本大小偏好的现实字体,这不起作用。为了理解我的意思,试试这个例子:

let button = UIButton(configuration: .plain())
let font = UIFontMetrics(forTextStyle: .subheadline)
    .scaledFont(for: UIFont(name: "Georgia", size: 16)!)
button.configuration?.title = "Hello"
button.configuration?.titleTextAttributesTransformer = .init { container in
    container.merging(AttributeContainer.font(font))
}


您将看到,尽管按钮标题最初以正确的字体显示,尽管字体在设置按钮的配置标题后仍然存在,但字体大小的 * 动态性 * 已经丢失。对于动态大小的字体,您必须将字体设置为属性标题的一部分。
事实上,这是最终激发了我最初的问题的用例,因此也是这个答案。

相关问题