如何在外部监听其他UIViewController的viewWillAppear等生命周期方法?
当然可以实现!
这种无需修改目标ViewController内部代码,从外部监听其生命周期方法(比如viewWillAppear或didLayoutSubviews)的需求,最常用且成熟的方案就是利用**Objective-C Runtime的方法交换(Method Swizzling)**特性。下面我会一步步给你讲清楚实现方式:
核心思路
方法交换的本质是替换类的方法实现:我们给UIViewController添加一个分类,在分类里实现一个自定义的"监听版"生命周期方法,然后在程序启动初期(比如类的初始化阶段),把系统原有的生命周期方法和我们的自定义方法做交换。这样当任何ViewController调用原生命周期方法时,都会先执行我们的自定义逻辑,再执行原方法的功能。
具体实现代码
第一步:给UIViewController添加分类,实现方法交换
import UIKit extension UIViewController { // 静态属性确保方法交换只执行一次(线程安全) static let swizzleSetup: Void = { // 监听viewWillAppear的情况 swizzleMethod(originalSelector: #selector(viewWillAppear(_:)), swizzledSelector: #selector(swizzled_viewWillAppear(_:))) // 如果要监听didLayoutSubviews,只需添加下面这行 // swizzleMethod(originalSelector: #selector(didLayoutSubviews), swizzledSelector: #selector(swizzled_didLayoutSubviews)) }() private static func swizzleMethod(originalSelector: Selector, swizzledSelector: Selector) { guard let originalMethod = class_getInstanceMethod(Self.self, originalSelector), let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else { return } // 交换两个方法的实现 method_exchangeImplementations(originalMethod, swizzledMethod) } // 自定义的viewWillAppear实现 @objc private func swizzled_viewWillAppear(_ animated: Bool) { // 先调用原方法的逻辑(因为已经交换了,调用这个方法其实是执行原来的viewWillAppear) self.swizzled_viewWillAppear(animated) // 发送通知,把当前ViewController作为参数传出去 NotificationCenter.default.post( name: NSNotification.Name("ViewControllerViewWillAppeared"), object: self ) } // 如果要监听didLayoutSubviews,添加这个方法 @objc private func swizzled_didLayoutSubviews() { self.swizzled_didLayoutSubviews() NotificationCenter.default.post( name: NSNotification.Name("ViewControllerDidLayoutSubviews"), object: self ) } }
第二步:在你的目标类里注册通知,监听指定VC的事件
比如在你持有someVC的类中:
class YourClass: UIViewController { var someVC: UIViewController! override func viewDidLoad() { super.viewDidLoad() // 触发方法交换(只需调用一次,放在任何地方都可以,比如AppDelegate的didFinishLaunching里也行) _ = UIViewController.swizzleSetup // 注册通知,只监听someVC的事件 NotificationCenter.default.addObserver( self, selector: #selector(wow), name: NSNotification.Name("ViewControllerViewWillAppeared"), object: someVC ) } @objc func wow(_ notification: Notification) { print("we spied on that view controller, and it just willAppeared") } deinit { // 记得移除观察者,避免内存泄漏 NotificationCenter.default.removeObserver(self) } }
关键注意事项
- 方法交换只执行一次:用静态属性的初始化器来执行交换逻辑,因为Swift中静态属性的初始化是线程安全且只会执行一次的,避免多次交换导致逻辑混乱。
- 精准监听单个VC:注册通知时指定
object: someVC,这样只会收到这个特定ViewController的事件,不会被其他VC的生命周期事件干扰。 - 保留原方法逻辑:在自定义的swizzled方法里一定要先调用自身(因为方法已经交换,这其实是在调用原系统方法),确保ViewController原有的生命周期逻辑不受影响。
- 内存管理:记得在持有观察者的类销毁时移除通知观察者,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

