UIColor initWithCoder 提取颜色名称

问题描述 投票:0回答:1

我正在尝试通过调配 UIColor 构造函数方法向 iOS 添加某种形式的主题支持。当控件使用“命名颜色”(手动添加到资产目录中的颜色)时,我在处理来自故事板的场景时遇到了问题。在到达我正在使用的

UIColor(named: ....)
构造函数之前打印我可以看到的堆栈跟踪,它调用
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
    }
}
ios swift storyboard nscoder method-swizzling
1个回答
2
投票

使用这个答案中的技术,我们可以通过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)
        }
    }
}

然后,您可以对颜色进行编码和解码,以查看解码了哪些键:

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

在 iOS 15.0 上,这会打印我在资产目录中添加的颜色:

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

从列表中并使用一些常识,

UIDynamicCatalogName
似乎是您正在寻找的关键。

请注意,这不适用于

systemRed
等基色或
systemBackground
等系统颜色。对于后者,您应该使用密钥
UISystemColorName
(您可以通过编码和解码
.systembackground
找到它)。对于前者,目前还不清楚使用什么密钥对其进行编码。通过编码和解码
.systemYellow
,我们看到与
.systembackground
相同的键,但使用
UISystemColorName
似乎不起作用。笔尖可能编码不同......

© www.soinside.com 2019 - 2024. All rights reserved.