Swizzling UIImage init not working iOS Swift

会有一股神秘感。 提交于 2020-02-24 18:36:24

问题


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)
    }
}

Usage

UIImage.swizzle()
let image = UIImage(named: "avatar")

👆 not working


回答1:


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")
    }
}


来源:https://stackoverflow.com/questions/60034039/swizzling-uiimage-init-not-working-ios-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!