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

Swift中UINavigationController子类调用父类navigationBar(_:shouldPop:)方法的实现方案

失败方案的核心原因

OC版本代码能正常运行,本质是依托UINavigationController本身就是自身navigationBar默认delegate、且已经内置实现navigationBar:shouldPopItem:方法的特性,子类重写该方法后调用super就是正常走父类的内置流程,没有delegate链路的额外问题。
Swift编译模型对这部分系统实现做了隐藏处理,才导致两次尝试全部失效:

  • 直接调用super.navigationBar(_:shouldPop:)报编译错误:Swift编译器没有把这个系统内部使用的delegate方法标记为可直接override的公开接口,调用super时找不到对应方法的声明。IDE自动补全添加的override关键字是误判,本质编译器根本不认为这个方法是父类提供的可重写成员。
  • 用super.perform(...)触发无限循环:runtime的方法查找流程会优先命中子类已经实现的同名selector,根本不会跳转至父类的原始实现,所以一调用就会重复触发自己写的方法,形成死循环。
正确实现方案

不需要在extension里额外声明遵守UINavigationBarDelegate,通过方法交换+保存原始IMP的方式,等价实现OC里的重写+super调用逻辑,可直接运行的参考代码:

final class TestNavigationController: UINavigationController {
    // 定义和原始方法完全匹配的C函数类型,用来安全调用原始IMP
    private typealias ShouldPopAction = @convention(c) (AnyObject, Selector, UINavigationBar, UINavigationItem) -> Bool
    // 提前获取UINavigationController内置的原始方法IMP
    private static let originalMethodIMP: IMP = {
        let originalSel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
        guard let method = class_getInstanceMethod(UINavigationController.self, originalSel) else {
            fatalError("找不到UINavigationController的shouldPop原始方法")
        }
        return method_getImplementation(method)
    }()
    // 把原始IMP转成可直接调用的闭包
    private lazy var callOriginalShouldPop: ShouldPopAction = {
        return unsafeBitCast(Self.originalMethodIMP, to: ShouldPopAction.self)
    }()
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        swizzleShouldPopMethod()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        swizzleShouldPopMethod()
    }
    // 方法交换:把自定义实现挂到系统的selector上
    private func swizzleShouldPopMethod() {
        let originalSel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
        let customSel = #selector(TestNavigationController.custom_navigationBar(_:shouldPop:))
        guard let originalMethod = class_getInstanceMethod(Self.self, originalSel) ?? class_getInstanceMethod(UINavigationController.self, originalSel),
              let customMethod = class_getInstanceMethod(Self.self, customSel) else {
            return
        }
        method_exchangeImplementations(originalMethod, customMethod)
    }
    // 自定义的拦截逻辑
    @objc private func custom_navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // 在这里写你自己的业务拦截逻辑,比如判断是否允许返回
        // ...
        // 调用原始的系统实现,完全等价于OC里的 [super navigationBar:navigationBar shouldPopItem:item]
        return callOriginalShouldPop(
            self,
            #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:)),
            navigationBar,
            item
        )
    }
}
实现注意事项
  • 不要通过extension额外声明遵守UINavigationBarDelegate,系统已经自动将UINavigationController设为自身导航栏的delegate,额外声明没有实际作用,反而可能打乱内置的delegate调用链路。
  • 自定义的方法签名要和系统原始方法完全匹配:不要给navigationBar参数添加可选标记,不要强制加自动补全带的override关键字,否则会因为签名不一致导致runtime识别不到对应方法,自定义逻辑完全失效。
  • 不需要保留之前的异步pop临时实现,这套基于IMP交换的方案可以完全对齐OC版本的原生逻辑,不会衍生额外的栈错乱问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 01:45:09