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

如何一次性重写UIKit所有视图控制器的prepare(for:sender:)方法

全局复用prepare(for:sender:)重写逻辑的可行方案(适配VIPER架构)

针对你在VIPER架构iOS开发中,想要一次性实现全局复用prepare(for:sender:)重写逻辑的需求,我整理了两个实用方案,不用每个视图控制器都重复写代码或者强制继承:


方案一:协议+默认扩展(推荐,可控性强)

这个方案可以让你选择性地让需要的视图控制器生效,不会影响系统或第三方的视图控制器,风险更低。

实现步骤:

  1. 定义一个空协议,用来承载默认实现:
protocol SegueClosureHandling: UIViewController {}
  1. 给协议添加默认扩展,实现你想要的prepare(for:sender:)逻辑:
extension SegueClosureHandling {
    func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let closure = sender as? (UIStoryboardSegue) -> Void {
            closure(segue)
        }
    }
}
  1. 让你的VIPER架构中所有视图控制器遵守这个协议即可:
class HomeViewController: UIViewController, SegueClosureHandling {
    // 如果不需要自定义prepare逻辑,这里不用写任何代码,自动使用协议扩展的实现
    
    // 如果需要添加自定义逻辑,可以重写prepare方法,并手动调用协议的默认实现:
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 先处理你的自定义逻辑,比如传递额外数据
        self.someCustomPrepareLogic()
        
        // 再调用协议的闭包处理逻辑
        (self as SegueClosureHandling).prepare(for: segue, sender: sender)
    }
    
    private func someCustomPrepareLogic() {
        // 你的自定义代码
    }
}

方案二:Method Swizzling(完全全局生效)

如果你希望所有UIViewController及其子类自动生效,不需要手动遵守协议,可以用方法交换的方式。但要注意,这种方式是全局生效的,可能会影响系统或第三方库的视图控制器,需要做好测试。

实现步骤:

  1. 创建UIViewController的分类,实现方法交换逻辑:
import UIKit

extension UIViewController {
    static func setupSegueClosureHandling() {
        let originalSelector = #selector(UIViewController.prepare(for:sender:))
        let swizzledSelector = #selector(swizzledPrepare(for:sender:))
        
        guard let originalMethod = class_getInstanceMethod(self, originalSelector),
              let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else {
            return
        }
        
        // 交换方法实现
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    
    @objc private func swizzledPrepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 先执行我们的闭包处理逻辑
        if let closure = sender as? (UIStoryboardSegue) -> Void {
            closure(segue)
        }
        // 再执行原来的prepare逻辑(方法交换后,调用这个方法其实是调用原有的实现)
        self.swizzledPrepare(for: segue, sender: sender)
    }
}
  1. 在App启动时执行方法交换,确保只执行一次:
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 全局启用segue闭包处理
        UIViewController.setupSegueClosureHandling()
        return true
    }
}

注意事项:

  • 方法交换是全局生效的,所有UIViewController子类都会受影响,如果某个子类自己重写了prepare(for:sender:),需要确保它调用了super.prepare(for:sender:),否则我们的闭包逻辑不会触发。
  • 务必确保方法交换只执行一次,避免多次交换导致逻辑混乱。

内容的提问来源于stack exchange,提问作者BlindJoeDeath

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:20:04