I am trying to swizzle UIImage.init(named:)
but the init is not being called
extension UIImage {
@objc public convenience init?(swizzledName: String) {
self.init(named: swizzledName)
/// Do something
print("this is working")
}
static func swizzle() {
guard let instance = class_getClassMethod(self, #selector(UIImage.init(named:))),
let swizzledInstance = class_getClassMethod(self, #selector(UIImage.init(swizzledName:))) else { return }
method_exchangeImplementations(instance, swizzledInstance)
}
}
UIImage.swizzle()
let image = UIImage(named: "avatar")
👆 not working
I too was having trouble swizzling these init
methods for UIImage
. The only way I found was to instead use class methods, which seem to work fine (I first tried with static
methods, but that didn't work, so then I tried class
methods, which did work):
func swizzle(originalClass: AnyClass,
originalSelector: Selector,
isOriginalSelectorClassMethod: Bool,
swizzledClass: AnyClass,
swizzledSelector: Selector,
isSwizzledSelectorClassMethod: Bool) {
guard let originalMethod = isOriginalSelectorClassMethod ?
class_getClassMethod(originalClass, originalSelector) :
class_getInstanceMethod(originalClass, originalSelector) else {
return
}
guard let swizzledMethod = isSwizzledSelectorClassMethod ?
class_getClassMethod(swizzledClass, swizzledSelector) :
class_getInstanceMethod(swizzledClass, swizzledSelector) else {
return
}
let didAddMethod = class_addMethod(isOriginalSelectorClassMethod ? object_getClass(originalClass)! : originalClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(isSwizzledSelectorClassMethod ? object_getClass(swizzledClass)! : swizzledClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
extension UIImage {
static func swizzleInitializersIfNeeded() {
guard !areInitializersSwizzled else {
return
}
areInitializersSwizzled = true
swizzle(originalClass: self,
originalSelector: #selector(UIImage.init(named:)),
isOriginalSelectorClassMethod: true,
swizzledClass: self,
swizzledSelector: #selector(UIImage.image(named:)),
isSwizzledSelectorClassMethod: true)
swizzle(originalClass: self,
originalSelector: #selector(UIImage.init(named:in:with:)),
isOriginalSelectorClassMethod: true,
swizzledClass: self,
swizzledSelector: #selector(UIImage.image(named:in:with:)),
isSwizzledSelectorClassMethod: true)
}
private static var areInitializersSwizzled = false
@objc fileprivate class func image(named name: String) -> UIImage? {
let image = self.image(named: name)
image?.name = name
return image
}
@objc fileprivate class func image(named name: String,
in bundle: Bundle,
with config: UIImage.Configuration) -> UIImage? {
let image = self.image(named: name, in: bundle, with: config)
image?.name = name
image?.bundle = bundle
return image
}
private static var nameKey = 0
private static var bundleKey = 0
private(set) var name: String? {
get { objc_getAssociatedObject(self, &UIImage.nameKey) as? String }
set { objc_setAssociatedObject(self, &UIImage.nameKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
private(set) var bundle: Bundle? {
get { objc_getAssociatedObject(self, &UIImage.bundleKey) as? Bundle }
set { objc_setAssociatedObject(self, &UIImage.bundleKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UIImage.swizzleInitializersIfNeeded()
let image = UIImage(named: "test_image")
print(image?.name as Any) // prints Optional("test_image")
}
}