我有一个图像,是动态的光/暗模式。如果我将此图像放置在UIImageView中,则动态工作:当用户从光模式切换到黑暗模式并返回时,图像会改变显示的自身版本。但是,如果我在NSAttributedString中放置与NSTextAttachment相同的图像,并将字符串显示在标签中,则动态无法工作:当用户从光模式切换到黑暗模式时,图像不会改变。
若要查看实际问题,请将此代码粘贴到viewDidLoad中。
let size = CGSize(width: 20, height: 20)
let renderer = UIGraphicsImageRenderer(size: size)
let image1 = renderer.image {
UIColor.red.setFill()
$0.fill(.init(origin: .zero, size: size))
}
let image2 = renderer.image {
UIColor.green.setFill()
$0.fill(.init(origin: .zero, size: size))
}
let asset = UIImageAsset()
asset.register(image1, with: .init(userInterfaceStyle: .light))
asset.register(image2, with: .init(userInterfaceStyle: .dark))
let iv = UIImageView(image: image1)
iv.frame.origin = .init(x: 100, y: 100)
self.view.addSubview(iv)
let text = NSMutableAttributedString(string: "Howdy ", attributes: [
.foregroundColor: UIColor(dynamicProvider: { traits in
switch traits.userInterfaceStyle {
case .light: return .red
case .dark: return .green
default: return .red
}
})
])
let attachment = NSTextAttachment(image: image1)
let attachmentCharacter = NSAttributedString(attachment: attachment)
text.append(attachmentCharacter)
let label = UILabel()
label.attributedText = text
label.sizeToFit()
label.frame.origin = .init(x: 100, y: 150)
self.view.addSubview(label)我故意使文本字体颜色动态,这样您就可以看到,一般说来,颜色动态在属性化字符串中确实有效。但不是在属性化字符串的文本附件中!
所以:这真的是iOS的行为方式吗,还是我在配置文本附件时犯了一些错误?如果这是iOS的行为方式,那么您是如何解决这个问题的?
请注意,我不能使用iOS 15附件视图提供程序,因为我必须与iOS 13和14兼容。
发布于 2022-02-07 14:52:28
我认为这是很不幸的正常行为,但我认为这是一个被苹果遗忘的非开发功能。
我目前得到的唯一方法是听func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)来检测模式的变化。
然后,您可以重构您的NSAttributedString,或者在需要时对其进行枚举和更新。
使用枚举,即只更新所需的内容,而不是重新生成整个NSAttributedString
在你最初的依恋创造中:
let attachment = NSTextAttachment(image: asset.image(with: traitCollection))
let attachmentCharacter = NSAttributedString(attachment: attachment)附带注意:我使用asset.image(with: traitCollection)而不是image1,否则当开始使用黑暗模式时,您的图像将是光模式。因此,这应该设置正确的图像。
然后,我会用以下方式更新:
func switchAttachment(for attr: NSAttributedString?) -> NSAttributedString? {
guard let attr = attr else { return nil }
let mutable = NSMutableAttributedString(attributedString: attr)
mutable.enumerateAttribute(.attachment, in: NSRange(location: 0, length: mutable.length), options: []) { attachment, range, stop in
guard let attachment = attachment as? NSTextAttachment else { return }
guard let asset = attachment.image?.imageAsset else { return }
attachment.image = asset.image(with: .current)
mutable.replaceCharacters(in: range, with: NSAttributedString(attachment: attachment))
}
return mutable
}并在下列情况下更新:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
label.attributedText = switchAttachment(for: label.attributedText)
}发布于 2022-02-07 16:22:07
为了记录在案,这是我对Larme建议的改写。我在UILabel上做了一个扩展:
extension UILabel {
func updateAttachments() {
guard let attributedString = attributedText else { return }
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
attributedString.enumerateAttribute(.attachment, in: .init(location: 0, length: attributedString.string.utf16.count), options: []) { value, range, stop in
guard let attachment = value as? NSTextAttachment else { return }
guard let image = attachment.image else { return }
guard let asset = image.imageAsset else { return }
attachment.image = asset.image(with: .current)
mutableAttributedString.setAttributes([.attachment: attachment], range: range)
}
attributedText = mutableAttributedString
}
}现在,假设我引用了一个麻烦的UILabel:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
guard let previousTraitCollection = previousTraitCollection else { return }
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
label.updateAttachments()
}
}将该覆盖注入到所有UILabels中非常好,这样他们就可以自己更新,但不幸的是,扩展不能这样工作,而且我不能将我的应用程序中的所有UILabels转换为UILabel子类。
https://stackoverflow.com/questions/71008562
复制相似问题