[英]NSTextAttachment images are not dynamic (light/dark mode)
我有一個動態圖像,適用於亮/暗模式。 如果我將此圖像放在 UIImageView 中,動態會起作用:當用戶從淺色模式切換到深色模式並返回時,圖像會更改顯示的自身版本。 但是,如果我在 NSAttributedString 中放置與 NSTextAttachment 相同的圖像,並在 label 中顯示該字符串,則動態不起作用:當用戶從亮模式切換到暗模式時,圖像不會改變。
要查看實際問題,請將此代碼粘貼到您的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 兼容。所以也許這可以解決問題,但該解決方案對我不開放。]
不幸的是,我認為這是正常行為,但我認為這是 Apple 非開發人員遺忘的功能。
我目前得到的唯一方法是聽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)
}
僅作記錄,這是我對 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()
}
}
將覆蓋注入所有 UILabel 非常好,這樣它們就可以自我更新,但不幸的是,擴展不能那樣工作,而且我無法將我應用程序中的所有 UILabel 轉換為 UILabel 子類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.