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

UISplitViewController主视图控制器rightBarButtonItem点击无响应问题

问题:UISplitViewController主列导航栏按钮点击无响应

用户代码

IpadLayoutCoordinator

class IpadLayoutCoordinator: BaseCoordinator {

    private let rootViewController: UINavigationController

    private lazy var splitVC: UISplitViewController = {
        let vc = UISplitViewController(style: .doubleColumn)
        vc.minimumPrimaryColumnWidth = 450.0
        vc.maximumPrimaryColumnWidth = 450.0
        return vc
    }()

    init(navController: UINavigationController) {
        self.rootViewController = navController
    }

    override func start() {
        let primaryViewCoordinator = PrimaryViewCoordinator(rootView: splitVC)
        primaryViewCoordinator.start()
        self.parentCoordinator?.store(coordinator: primaryViewCoordinator)

        let secondaryView = UIViewController()
        secondaryView.view.backgroundColor = .blueColor           
        splitVC.setViewController(secondaryView, for: .secondary)
    
        let containerVC = UIViewController()
        containerVC.addChildViewController(splitVC, toView: containerVC.view)
        rootViewController.setViewControllers([containerVC], animated: true)
    }

}

PrimaryViewCoordinator

class PrimaryViewCoordinator: BaseCoordinator {
    
    private let rootViewController: UISplitViewController
    
    private lazy var primaryVC: PrimaryViewController = {
        let vc = PrimaryViewController.loadFromNib()
    
        /* This is the button not working */
        let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(self.goToAddVC))
        vc.navigationItem.rightBarButtonItem = addButton
    
        return vc
    }()

    init(rootView: UISplitViewController) {
        self.rootViewController = rootView
    }

    override func start() {
        rootViewController.setViewController(primaryVC, for: .primary)
    }

    @objc private func goToAddVC() {
        /* This is the action not working */
        let addItemCoordinator = AddItemCoordinator(rootView: rootViewController)
        addItemCoordinator.start()
        self.store(coordinator: addItemCoordinator)
    }

}

问题原因

核心问题是PrimaryViewCoordinator实例被提前释放:

  • 在IpadLayoutCoordinator的start方法中,primaryViewCoordinator是局部变量,尽管调用了self.parentCoordinator?.store(coordinator: primaryViewCoordinator),但如果BaseCoordinator的store方法没有强引用子协调器,或者parentCoordinator的引用链存在问题,这个实例会在方法执行完毕后被ARC回收。
  • 导航栏按钮的target指向PrimaryViewCoordinator实例,当实例被释放后,点击事件找不到接收者,自然无法触发goToAddVC方法。

修复方案

方案1:在IpadLayoutCoordinator中强引用子协调器

修改IpadLayoutCoordinator,添加属性持有primaryViewCoordinator,避免其被释放:

class IpadLayoutCoordinator: BaseCoordinator {

    private let rootViewController: UINavigationController
    // 添加强引用属性
    private var primaryViewCoordinator: PrimaryViewCoordinator?

    private lazy var splitVC: UISplitViewController = {
        let vc = UISplitViewController(style: .doubleColumn)
        vc.minimumPrimaryColumnWidth = 450.0
        vc.maximumPrimaryColumnWidth = 450.0
        return vc
    }()

    init(navController: UINavigationController) {
        self.rootViewController = navController
    }

    override func start() {
        let coordinator = PrimaryViewCoordinator(rootView: splitVC)
        coordinator.start()
        self.primaryViewCoordinator = coordinator // 强引用保存
        self.parentCoordinator?.store(coordinator: coordinator)

        let secondaryView = UIViewController()
        secondaryView.view.backgroundColor = .blueColor           
        splitVC.setViewController(secondaryView, for: .secondary)
    
        let containerVC = UIViewController()
        containerVC.addChildViewController(splitVC, toView: containerVC.view)
        rootViewController.setViewControllers([containerVC], animated: true)
    }

}

方案2:确保BaseCoordinator的store方法正确强引用子协调器

检查BaseCoordinator的实现,确保它通过数组强引用子协调器:

class BaseCoordinator {
    var childCoordinators = [BaseCoordinator]()
    
    func store(coordinator: BaseCoordinator) {
        childCoordinators.append(coordinator)
    }
    
    // 可选:添加移除方法避免内存泄漏
    func free(coordinator: BaseCoordinator) {
        childCoordinators.removeAll { $0 === coordinator }
    }
    
    func start() {
        // 子类实现
    }
    
    weak var parentCoordinator: BaseCoordinator?
}

之后修改IpadLayoutCoordinator的start方法,让当前协调器直接持有子协调器:

override func start() {
    let primaryViewCoordinator = PrimaryViewCoordinator(rootView: splitVC)
    primaryViewCoordinator.start()
    self.store(coordinator: primaryViewCoordinator) // 当前协调器持有
    // self.parentCoordinator?.store(coordinator: primaryViewCoordinator) 可按需保留或移除

    // ... 其余代码不变
}

额外检查点

  • 确认PrimaryViewController的navigationItem没有被nib文件或其他代码覆盖,导致代码添加的按钮被替换。
  • 若primaryVC未被包裹在UINavigationController中,可尝试将其包裹后再设置给splitVC:
override func start() {
    let navVC = UINavigationController(rootViewController: primaryVC)
    rootViewController.setViewController(navVC, for: .primary)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:50:23