UIColor initWithCoder 提取颜色名称
UIColor initWithCoder extract color name
我正在尝试通过混合 UIColor 构造函数方法向 iOS 添加一种主题支持形式。当控件使用“命名颜色”(手动添加到资产目录的颜色)时,我遇到了来自情节提要的场景的问题。在到达我正在使用的 UIColor(named: ....)
构造函数之前打印我可以看到的堆栈跟踪,它调用 UIColor initWithCoder
。我想如果我可以改用它并提取在情节提要中输入的“名称”,它将解决我的问题。 (我知道这个值将在另一个构造函数中传递,但我需要看看它是否可能来自编码器构造函数,长话短说)
我正在努力了解如何从 NSCoder
中获取此值。我找不到关于其基础类型 UINibDecoder
的任何信息,调试器没有提供任何有用的信息,我不知道对象使用什么 keys/types,所以我无法调用 coder.decode...
函数,我在网上找不到任何关于如何打印所有 keys/types 的信息。任何见解/帮助将不胜感激
我目前拥有的代码大致如下:
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
}
}
使用this answer中的技巧,我们可以通过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
似乎不起作用。笔尖可能编码不同...
我正在尝试通过混合 UIColor 构造函数方法向 iOS 添加一种主题支持形式。当控件使用“命名颜色”(手动添加到资产目录的颜色)时,我遇到了来自情节提要的场景的问题。在到达我正在使用的 UIColor(named: ....)
构造函数之前打印我可以看到的堆栈跟踪,它调用 UIColor initWithCoder
。我想如果我可以改用它并提取在情节提要中输入的“名称”,它将解决我的问题。 (我知道这个值将在另一个构造函数中传递,但我需要看看它是否可能来自编码器构造函数,长话短说)
我正在努力了解如何从 NSCoder
中获取此值。我找不到关于其基础类型 UINibDecoder
的任何信息,调试器没有提供任何有用的信息,我不知道对象使用什么 keys/types,所以我无法调用 coder.decode...
函数,我在网上找不到任何关于如何打印所有 keys/types 的信息。任何见解/帮助将不胜感激
我目前拥有的代码大致如下:
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
}
}
使用this answer中的技巧,我们可以通过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
似乎不起作用。笔尖可能编码不同...