如何复用导航栏按钮?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添加这个功能时,用扩展更灵活。
如果希望整个导航栈里的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
相关产品推荐
相关产品推荐

