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

SwiftUI中如何用系统原生UIBarButtonItem实现导航栏Cancel/Done按钮?

在SwiftUI中使用系统原生导航栏Cancel/Done按钮

没问题,我来帮你搞定这个事儿!你完全不用自己手动维护多语言翻译,SwiftUI里照样能复用系统原生的导航栏按钮,而且实现起来比你想的简单——根本不用绕UIViewRepresentable,直接用SwiftUI的Toolbar就能搞定。

最简洁的实现方案(推荐)

直接用Text搭配LocalizedStringKey,SwiftUI会自动匹配系统原生的本地化翻译,效果和UIKit里用UIBarButtonSystemItem完全一致:

import SwiftUI

struct SettingsDialog: View {
    // 假设用这个绑定控制对话框的显示/隐藏
    @Binding var isShowingDialog: Bool
    
    var body: some View {
        NavigationStack { // iOS 16及以上用NavigationStack,iOS 13-15换成NavigationView
            Form {
                // 这里放你的可编辑设置内容,比如文本输入、开关等
                Section {
                    TextField("昵称", text: .constant(""))
                    Toggle("启用夜间模式", isOn: .constant(false))
                }
            }
            .navigationTitle("设置")
            .toolbar {
                // 左侧Cancel按钮,调用系统原生翻译
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: {
                        isShowingDialog = false
                        // 这里加Cancel按钮的逻辑,比如放弃修改
                    }) {
                        Text(.init("Cancel"))
                    }
                }
                
                // 右侧Done按钮,同样用系统原生翻译
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        isShowingDialog = false
                        // 这里加Done按钮的逻辑,比如保存设置
                    }) {
                        Text(.init("Done"))
                    }
                }
            }
        }
    }
}

// 预览用
struct SettingsDialog_Previews: PreviewProvider {
    static var previews: some View {
        SettingsDialog(isShowingDialog: .constant(true))
    }
}

为什么这个方法能拿到原生翻译?

当你给Text传入LocalizedStringKey(比如Text(.init("Cancel"))),SwiftUI会自动去查找系统级的本地化字符串,和UIKit里用UIBarButtonSystemItem.cancel得到的翻译完全一样,不用你手动维护多语言文件。

如果你偏好用UIKit的UIBarButtonItem(备选方案)

如果你就是想完全复用UIKit的UIBarButtonItem,也可以用UIViewControllerRepresentable做个小封装,不过这个方案不如上面的简洁,仅供参考:

struct SystemBarButton: UIViewControllerRepresentable {
    let systemItem: UIBarButtonItem.SystemItem
    let onTap: () -> Void
    
    func makeUIViewController(context: Context) -> UIViewController {
        let vc = UIViewController()
        let barButton = UIBarButtonItem(barButtonSystemItem: systemItem, target: context.coordinator, action: #selector(Coordinator.handleTap))
        // 这里可以改leftBarButtonItem或rightBarButtonItem
        vc.navigationItem.leftBarButtonItem = barButton
        return vc
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(action: onTap)
    }
    
    class Coordinator: NSObject {
        private let action: () -> Void
        
        init(action: @escaping () -> Void) {
            self.action = action
        }
        
        @objc func handleTap() {
            action()
        }
    }
}

// 使用的时候这样写
ToolbarItem(placement: .navigationBarLeading) {
    SystemBarButton(systemItem: .cancel) {
        isShowingDialog = false
    }
}

总的来说,第一种方案更符合SwiftUI的设计风格,代码更简洁,还能完美拿到系统原生的多语言支持,优先推荐用这个~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:52:51