如何一次性重写UIKit所有视图控制器的prepare(for:sender:)方法
全局复用
prepare(for:sender:)重写逻辑的可行方案(适配VIPER架构) 针对你在VIPER架构iOS开发中,想要一次性实现全局复用prepare(for:sender:)重写逻辑的需求,我整理了两个实用方案,不用每个视图控制器都重复写代码或者强制继承:
方案一:协议+默认扩展(推荐,可控性强)
这个方案可以让你选择性地让需要的视图控制器生效,不会影响系统或第三方的视图控制器,风险更低。
实现步骤:
- 定义一个空协议,用来承载默认实现:
protocol SegueClosureHandling: UIViewController {}
- 给协议添加默认扩展,实现你想要的
prepare(for:sender:)逻辑:
extension SegueClosureHandling { func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let closure = sender as? (UIStoryboardSegue) -> Void { closure(segue) } } }
- 让你的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及其子类自动生效,不需要手动遵守协议,可以用方法交换的方式。但要注意,这种方式是全局生效的,可能会影响系统或第三方库的视图控制器,需要做好测试。
实现步骤:
- 创建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) } }
- 在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
相关产品推荐
相关产品推荐

