uicolor initwithCoder提取物颜色名称

发布于 2025-01-23 18:29:42 字数 1934 浏览 2 评论 0 原文

我正在尝试通过浏览Uicolor构造方法向iOS添加主题支持的形式。当一个控件使用“命名颜色”(手动添加到资产目录)时,我的故事板的情况遇到了麻烦。打印堆栈跟踪,我可以看到它到达 uicolor(命名:....)我正在使用的构造器,它调用 uicolor initwithCoder 。我认为,如果我可以改用它并提取故事板中输入的“名称”,那将解决我的问题。 (我知道这个值将在另一个构造函数中传递,但是我需要看看编码器构造函数是否可以从编码器构造函数,长篇小说),

我正在努力理解如何从 nscoder中获取该值。我找不到有关其基础类型的 uinibdecoder 的任何信息,调试器没有显示任何有用的信息,我不知道该对象使用的键/类型,因此我无法称呼<<<代码> coder.decode ... 函数,我在网上找不到有关如何打印所有键/类型的任何东西。

我目前拥有的代码是类似的:

extension UIColor {
    private static let originalCoderSelector = #selector(UIColor.init(coder:))
    private static let swizzledCoderSelector = #selector(theme_color(withCoder:))
    
    class func swizzleNamedColorInitToAddTheme() {
        guard let originalCoderMethod = class_getInstanceMethod(self, originalCoderSelector),
              let swizzledCoderMethod = class_getInstanceMethod(self, swizzledCoderSelector) else {
            os_log("Unable to find UIColor methods to swizzle", log: .default, type: .error)
            return
        }
        
        method_exchangeImplementations(originalCoderMethod, swizzledCoderMethod);
    }

    @objc func theme_color(withCoder coder: NSCoder) -> UIColor? {
        print("coder being called")
        
        print("coder test: \( coder.decodeObject(forKey: "backgroundColor") ) ")
        print("coder test: \( coder.decodeObject(forKey: "color") ) ")
        print("coder test: \( coder.decodeObject(forKey: "name") ) ")
        print("coder test: \( coder.decodeObject(forKey: "namedColor") ) ")
        
        print("coder test: \( coder.decodePropertyList(forKey: "backgroundColor") ) ")
        print("coder test: \( coder.decodePropertyList(forKey: "color") ) ")
        print("coder test: \( coder.decodePropertyList(forKey: "name") ) ")
        print("coder test: \( coder.decodePropertyList(forKey: "namedColor") ) ")
        
        return nil
    }
}

I'm experimenting with adding a form of theming support to iOS by swizzling UIColor constructor methods. I'm having trouble with a scenario coming from a storyboard when a control uses a "named color" (the ones added to the asset catalog manually). Printing the stack trace I can see before it gets to the UIColor(named: ....) constructor which I was using, it calls UIColor initWithCoder. I think if I can swizzle this instead and extract the "name" that was entered in the storyboard, it will solve my issue. (I'm aware this value will be passed in the other constructor, but I need to see if it's possible from the coder constructor instead, long story)

I'm struggling to understand how to get this value out of the NSCoder. I can't find any info on its underlying type of UINibDecoder, debugger doesn't present any useful info, I don't know what keys/types the object uses so I can't call the coder.decode... functions, and I can't find anything online regarding how to print all the keys/types.

Code I have currently is something along the lines of:

extension UIColor {
    private static let originalCoderSelector = #selector(UIColor.init(coder:))
    private static let swizzledCoderSelector = #selector(theme_color(withCoder:))
    
    class func swizzleNamedColorInitToAddTheme() {
        guard let originalCoderMethod = class_getInstanceMethod(self, originalCoderSelector),
              let swizzledCoderMethod = class_getInstanceMethod(self, swizzledCoderSelector) else {
            os_log("Unable to find UIColor methods to swizzle", log: .default, type: .error)
            return
        }
        
        method_exchangeImplementations(originalCoderMethod, swizzledCoderMethod);
    }

    @objc func theme_color(withCoder coder: NSCoder) -> UIColor? {
        print("coder being called")
        
        print("coder test: \( coder.decodeObject(forKey: "backgroundColor") ) ")
        print("coder test: \( coder.decodeObject(forKey: "color") ) ")
        print("coder test: \( coder.decodeObject(forKey: "name") ) ")
        print("coder test: \( coder.decodeObject(forKey: "namedColor") ) ")
        
        print("coder test: \( coder.decodePropertyList(forKey: "backgroundColor") ) ")
        print("coder test: \( coder.decodePropertyList(forKey: "color") ) ")
        print("coder test: \( coder.decodePropertyList(forKey: "name") ) ")
        print("coder test: \( coder.decodePropertyList(forKey: "namedColor") ) ")
        
        return nil
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

走野 2025-01-30 18:29:42

获得编码密钥

extension NSKeyedUnarchiver {
    private static let originalDecodeObject = #selector(NSKeyedUnarchiver.decodeObject(forKey:))
    private static let swizzledDecodeObject = #selector(swizzledDecodeObject)

    @objc
    func swizzledDecodeObject(forKey key: String) -> Any? {
        print("Key:", key)
        return swizzledDecodeObject(forKey: key)
    }

    class func swizzle() {
        if let orig = class_getInstanceMethod(self, originalDecodeObject),
           let new = class_getInstanceMethod(self, swizzledDecodeObject) {
            method_exchangeImplementations(orig, new)
        }
    }
}

,我们可以通过swizzling nskeyedunarchiver.decodeobject (

let data = try! NSKeyedArchiver.archivedData(withRootObject: UIColor(named: "foo"), requiringSecureCoding: false)
let decoded = try! NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)

forkey 对于我在资产目录中添加的颜色:

Key: root
Key: UIDynamicModifiedBaseColor
Key: UIDynamicCatalogName
Key: UIDynamicCatalogBundleIdentifier
Key: UIDynamicCatalogBundleLibraryName

从列表中并使用一些常识, uidyynamicCataLogname 似乎是您想要的关键。

请注意,这对基本颜色不起作用,例如 Systemred 或系统颜色,例如 SystemBackground 。对于后者,您应该使用键 uisystemColorname (您可以通过编码和解码 .Systembackground )找到此问题。对于前者,仍不清楚使用什么密钥来编码它。通过编码和解码 .systemyellow ,我们看到了与 .Systembackground 的键,但是使用 uisystemcolorname 似乎不起作用。笔尖的编码可能不同...

Using the technique in this answer, we can get the coding keys by swizzling NSKeyedUnarchiver.decodeObject(forKey:):

extension NSKeyedUnarchiver {
    private static let originalDecodeObject = #selector(NSKeyedUnarchiver.decodeObject(forKey:))
    private static let swizzledDecodeObject = #selector(swizzledDecodeObject)

    @objc
    func swizzledDecodeObject(forKey key: String) -> Any? {
        print("Key:", key)
        return swizzledDecodeObject(forKey: key)
    }

    class func swizzle() {
        if let orig = class_getInstanceMethod(self, originalDecodeObject),
           let new = class_getInstanceMethod(self, swizzledDecodeObject) {
            method_exchangeImplementations(orig, new)
        }
    }
}

Then, you can encode and decode a color to see what keys are decoded:

let data = try! NSKeyedArchiver.archivedData(withRootObject: UIColor(named: "foo"), requiringSecureCoding: false)
let decoded = try! NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)

On iOS 15.0, this prints for a color I added in the asset catalog:

Key: root
Key: UIDynamicModifiedBaseColor
Key: UIDynamicCatalogName
Key: UIDynamicCatalogBundleIdentifier
Key: UIDynamicCatalogBundleLibraryName

From the list and using some common sense, UIDynamicCatalogName seems to be the key that you are looking for.

Note that this won't work for base colours like systemRed, or system colours like systemBackground. For the latter, you should use the key UISystemColorName (you can find this by encoding and decoding .systembackground). For the former, it is still unclear what key is used to encode it. By encoding and decoding .systemYellow, we see about the same keys as .systembackground, but using UISystemColorName doesn't seem to work. The nib is probably encoded differently...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文