自定义导航栏(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
相关产品推荐
相关产品推荐

