Я экспериментирую с добавлением формы поддержки тем для iOS, используя методы конструктора UIColor. У меня возникли проблемы со сценарием, исходящим из раскадровки, когда элемент управления использует «именованный цвет» (те, которые добавляются в каталог активов вручную). Распечатывая трассировку стека, которую я вижу, прежде чем она попадет в конструктор 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
}
}
Используя технику этот ответ, мы можем получить ключи кодирования, прокручивая 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
, похоже, не работает. Перо, вероятно, закодировано по-другому...
Это какое-то удивительное колдовство. Благодарю вас!
@Sweeper спасибо, но в каталоге активов, который я в основном ищу, он называется Color’s. Это может быть полезно добавить к этому, если я получу то, что ищу.