以前 Method Swizzling 的时候需要在 load 或者 initialize 方法,但是在 Swift 中不能使用了。那就只能自己定义一个了。
extension UIViewController {
public class func initializeMethod() {
if self != UIViewController.self {
return
}
// Method Swizzling
DispatchQueue.once(token: "ChangeIcon") {
let orignal = class_getInstanceMethod(self, #selector(UIViewController.present(_:animated:completion:)))
let swizzling = class_getInstanceMethod(self, #selector(UIViewController.jt_present(_:animated:completion:)))
if let old = orignal, let new = swizzling {
method_exchangeImplementations(old, new)
}
}
}
@objc private func jt_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
// 在这里判断是否是更换icon时的弹出框
if viewControllerToPresent is UIAlertController {
let alertTitle = (viewControllerToPresent as! UIAlertController).title
let alertMessage = (viewControllerToPresent as! UIAlertController).message
// 更换icon时的弹出框,这两个string都为nil。
if alertTitle == nil && alertMessage == nil {
return
}
}
// 因为方法已经交换,这个地方的调用就相当于调用原先系统的 present
self.jt_present(viewControllerToPresent, animated: flag, completion: completion)
}
}
定义完 UIViewController 的扩展方法后,记得在 AppDelegate 中调用一下。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIViewController.initializeMethod()
return true
}
因为,Swift 中 GCD 之前的 once 函数没有了,这里自己简单定义了一个。
extension DispatchQueue {
private static var _onceTracker = [String]()
public class func once(token: String, block: () -> ()) {
objc_sync_enter(self)
defer {
objc_sync_exit(self)
}
if _onceTracker.contains(token) {
return
}
_onceTracker.append(token)
block()
}
}