簡體   English   中英

NSTextAttachment 圖像不是動態的(亮/暗模式)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM