You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在外部监听其他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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:03:05