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

自定义导航栏(Custom Navigation Bar)中UIView.xib按钮跳转失效问题

自定义UIView内按钮跳转失效问题解决

在UIViewController.xib中嵌入了自定义UIView.xib(对应CustomNavigationBar类),点击该视图内的按钮时,print语句正常执行,但页面跳转功能完全失效。

原代码错误分析

  • 第一种写法中,let nav = UINavigationController()是全新创建的导航控制器实例,这个实例从未被添加到应用的视图层级中,也没有管理任何正在显示的页面,因此nav.navigationController的值为nil,调用pushViewController自然不会有任何效果。
  • 第二种写法中,let nav = FirstViewController()同样是新创建的视图控制器实例,并非当前屏幕上正在显示的那个FirstViewController,它的navigationController属性也是nil,同样无法完成跳转。

方案1:通过代理模式让父控制器处理跳转(推荐)

自定义视图只负责触发事件,跳转逻辑交给所属的父视图控制器处理,符合单一职责原则。

修改CustomNavigationBar代码:

import UIKit

// 定义代理协议
protocol CustomNavigationBarDelegate: AnyObject {
    func navigationBarDidTapAlertButton()
}

final class CustomNavigationBar: UIView {
    // 弱引用代理,避免循环引用
    weak var delegate: CustomNavigationBarDelegate?

    @IBAction func buttonNavBar2(_ sender: UIButton) {
        print("BUTTON ALERT")
        // 通知代理执行跳转
        delegate?.navigationBarDidTapAlertButton()
    }
}

在嵌入该视图的父视图控制器(如FirstViewController)中实现代理:

class FirstViewController: UIViewController, CustomNavigationBarDelegate {
    // 连接xib中的自定义导航栏
    @IBOutlet weak var customNavBar: CustomNavigationBar!

    override func viewDidLoad() {
        super.viewDidLoad()
        // 设置代理
        customNavBar.delegate = self
    }

    // 实现代理方法,处理跳转逻辑
    func navigationBarDidTapAlertButton() {
        let detailVC = AlertViewController()
        navigationController?.pushViewController(detailVC, animated: true)
    }
}

方案2:获取应用顶层导航控制器跳转

如果不想使用代理,可以通过UIApplication获取当前应用的顶层导航控制器来完成跳转:

final class CustomNavigationBar: UIView {
    @IBAction func buttonNavBar2(_ sender: UIButton) {
        print("BUTTON ALERT")
        let detailVC = AlertViewController()
        
        // 获取当前keyWindow的根导航控制器
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
              let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }),
              let topNav = keyWindow.rootViewController as? UINavigationController else {
            return
        }
        
        topNav.pushViewController(detailVC, animated: true)
    }
}

如果根控制器不是导航控制器,而是普通视图控制器,可以扩展UIViewController来查找顶层视图控制器:

extension UIViewController {
    func topMostViewController() -> UIViewController {
        if let presentedVC = presentedViewController {
            return presentedVC.topMostViewController()
        }
        return self
    }
}

// 按钮点击方法中使用:
@IBAction func buttonNavBar2(_ sender: UIButton) {
    print("BUTTON ALERT")
    let detailVC = AlertViewController()
    guard let topVC = UIApplication.shared.connectedScenes
            .compactMap({ $0 as? UIWindowScene })
            .first?.windows
            .first(where: { $0.isKeyWindow })?.rootViewController?.topMostViewController() else {
        return
    }
    topVC.navigationController?.pushViewController(detailVC, animated: true)
}

方案3:遍历父视图找到所属控制器

通过自定义视图的父视图链,找到它所属的视图控制器,再使用该控制器的navigationController进行跳转:

final class CustomNavigationBar: UIView {
    @IBAction func buttonNavBar2(_ sender: UIButton) {
        print("BUTTON ALERT")
        let detailVC = AlertViewController()
        
        // 遍历父视图,找到所属的视图控制器
        var parentViewController: UIViewController?
        var currentView: UIView? = self
        while currentView != nil {
            if let vc = currentView?.next as? UIViewController {
                parentViewController = vc
                break
            }
            currentView = currentView?.superview
        }
        
        // 执行跳转
        parentViewController?.navigationController?.pushViewController(detailVC, animated: true)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:55:15