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

如何复用导航栏按钮?iOS导航控制器多页面开发疑问

嘿,针对你这个导航栏刷新按钮(连同余额标签)的复用需求,我给你整理了几个iOS开发里常用且实用的方案,你可以根据项目的结构和需求来选择:

方案1:创建基类ViewController统一管理

这是最直观、代码复用性最高的方式——把导航栏元素的创建、刷新逻辑都封装到一个基类里,需要用到这些功能的ViewController直接继承这个基类就行。

代码示例:

首先创建基类:

class BaseBalanceViewController: UIViewController {
    // 复用的余额标签
    var balanceLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationBarComponents()
    }
    
    // 统一创建导航栏元素
    private func setupNavigationBarComponents() {
        // 初始化余额标签
        balanceLabel = UILabel()
        balanceLabel.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
        balanceLabel.textColor = .label
        navigationItem.leftBarButtonItem = UIBarButtonItem(customView: balanceLabel)
        
        // 初始化刷新按钮
        let refreshButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshBalanceAction))
        navigationItem.rightBarButtonItem = refreshButton
    }
    
    // 通用的刷新逻辑,子类可以重写自定义行为
    @objc func refreshBalanceAction() {
        let operation = GetInfoOperation(/* 这里可以传入通用参数,子类重写时替换为自己的参数 */)
        operation.completionBlock = { [weak self] in
            DispatchQueue.main.async {
                guard let balance = operation.output?.value?.balance else { return }
                self?.balanceLabel.text = "余额:\(balance)"
            }
        }
        OperationQueue().addOperation(operation)
    }
}

然后你的第一个ViewController就可以这样用:

class FirstViewController: BaseBalanceViewController {
    // 如果需要自定义刷新逻辑,直接重写基类方法即可
    override func refreshBalanceAction() {
        // 这里写当前VC特有的请求逻辑,或者调用super保持通用逻辑
        let operation = GetInfoOperation(/* 传入当前VC需要的参数 */)
        operation.completionBlock = { [weak self] in
            DispatchQueue.main.async {
                guard let balance = operation.output?.value?.balance else { return }
                self?.balanceLabel.text = "当前账户余额:\(balance)"
            }
        }
        OperationQueue().addOperation(operation)
    }
}

适用场景:

多个ViewController的导航栏逻辑相似,且没有复杂的继承链冲突时,优先选这个方案,逻辑集中,维护起来也方便。

方案2:用UIViewController扩展封装复用逻辑

如果你的ViewController已经继承了其他类(没法再继承基类),可以用扩展来封装导航栏元素的创建和点击逻辑,灵活性更高。

代码示例:

extension UIViewController {
    // 调用这个方法来设置导航栏元素,返回余额标签方便后续更新
    func setupBalanceNavigationElements(withRefreshHandler handler: @escaping () -> Void) -> UILabel {
        // 创建余额标签
        let balanceLabel = UILabel()
        balanceLabel.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
        balanceLabel.textColor = .label
        navigationItem.leftBarButtonItem = UIBarButtonItem(customView: balanceLabel)
        
        // 创建刷新按钮
        let refreshButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshTriggered))
        navigationItem.rightBarButtonItem = refreshButton
        
        // 用关联对象保存刷新回调
        objc_setAssociatedObject(self, &RefreshHandlerKey, handler, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        
        return balanceLabel
    }
    
    @objc private func refreshTriggered() {
        if let handler = objc_getAssociatedObject(self, &RefreshHandlerKey) as? () -> Void {
            handler()
        }
    }
}

// 用于关联对象的唯一Key
private var RefreshHandlerKey: UInt8 = 0

然后在你的ViewController里调用:

class FirstViewController: UIViewController {
    private var balanceLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 设置导航栏元素,并传入刷新回调
        balanceLabel = setupBalanceNavigationElements { [weak self] in
            self?.performBalanceRefresh()
        }
    }
    
    private func performBalanceRefresh() {
        let operation = GetInfoOperation(...)
        operation.completionBlock = { [weak self] in
            DispatchQueue.main.async {
                guard let balance = operation.output?.value?.balance else { return }
                self?.balanceLabel.text = "余额:\(balance)"
            }
        }
        OperationQueue().addOperation(operation)
    }
}

适用场景:

不想改变现有继承链,或者只需要给少数几个ViewController添加这个功能时,用扩展更灵活。

方案3:自定义UINavigationController统一管理

如果希望整个导航栈里的ViewController都能统一复用这个导航栏元素,或者需要全局控制显示逻辑,可以自定义UINavigationController,通过协议来规范ViewController的行为。

代码示例:

首先定义协议,约定需要刷新功能的ViewController要实现的内容:

protocol BalanceRefreshable: UIViewController {
    var balanceLabel: UILabel? { get set }
    func refreshBalance()
}

// 协议的默认实现(可选,提供通用逻辑)
extension BalanceRefreshable {
    func refreshBalance() {
        let operation = GetInfoOperation(...)
        operation.completionBlock = { [weak self] in
            DispatchQueue.main.async {
                guard let balance = operation.output?.value?.balance else { return }
                self?.balanceLabel?.text = "余额:\(balance)"
            }
        }
        OperationQueue().addOperation(operation)
    }
}

然后自定义导航控制器:

class BalanceNavigationController: UINavigationController {
    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        super.pushViewController(viewController, animated: animated)
        // 给实现了协议的ViewController添加导航栏元素
        if let balanceVC = viewController as? BalanceRefreshable {
            setupBalanceNavigationElements(for: balanceVC)
        }
    }
    
    private func setupBalanceNavigationElements(for vc: BalanceRefreshable) {
        let balanceLabel = UILabel()
        balanceLabel.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
        balanceLabel.textColor = .label
        vc.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: balanceLabel)
        
        let refreshButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: vc, action: #selector(vc.refreshBalance))
        vc.navigationItem.rightBarButtonItem = refreshButton
        
        // 把标签传给ViewController,方便后续更新
        vc.balanceLabel = balanceLabel
    }
}

最后让你的ViewController实现协议:

class FirstViewController: UIViewController, BalanceRefreshable {
    var balanceLabel: UILabel?
    
    // 可以重写refreshBalance方法自定义逻辑
    func refreshBalance() {
        let operation = GetInfoOperation(/* 自定义参数 */)
        operation.completionBlock = { [weak self] in
            DispatchQueue.main.async {
                guard let balance = operation.output?.value?.balance else { return }
                self?.balanceLabel?.text = "我的账户余额:\(balance)"
            }
        }
        OperationQueue().addOperation(operation)
    }
}

适用场景:

整个导航栈的ViewController都需要这类导航栏元素,或者需要统一控制导航栏的显示逻辑时,这个方案更合适。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:46:04