SwiftUI中如何设置导航栏项样式?适配系统样式变化
Great question! This is a common pain point when transitioning from UIKit to SwiftUI—since the old navigationBarItems API only accepts views without built-in style controls, manually mimicking styles like the bold Done button can break when Apple updates system designs. Here are the most reliable solutions, sorted by iOS compatibility:
Option 1: iOS 14+ Use Toolbar with System Button Styles
Starting in iOS 14, Apple recommends replacing navigationBarItems with the toolbar modifier, which plays nicer with system styles. You can use a plain Button and apply the .borderless button style to match native navigation bar behavior:
struct ContentView: View { var body: some View { NavigationStack { // Use NavigationView for iOS 13 Text("Hello, SwiftUI!") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Done") { // Handle tap action } .buttonStyle(.borderless) // Matches native bar button interaction } } } } }
But for full alignment with UIKit's UIBarButtonItem.Style.done—including automatic adaptation to system font weights, color shifts, and future design changes—wrap a UIKit bar button directly.
Option 2: Full iOS Version Compatibility (Wrap UIKit's UIBarButtonItem)
This approach reuses UIKit's built-in style logic, so your Done button will always match the system's native look, no matter how Apple updates it. Create a SwiftUI wrapper for UIBarButtonItem:
struct SystemBarButton: UIViewRepresentable { let title: String let style: UIBarButtonItem.Style let action: () -> Void func makeUIView(context: Context) -> UIButton { let barButton = UIBarButtonItem(title: title, style: style, target: context.coordinator, action: #selector(Coordinator.handleTap)) let button = UIButton(type: .system) // Apply the bar button's style to the SwiftUI-compatible button barButton.configureButton(button) return button } func updateUIView(_ uiView: UIButton, context: Context) { uiView.setTitle(title, for: .normal) } func makeCoordinator() -> Coordinator { Coordinator(action: action) } class Coordinator: NSObject { private let action: () -> Void init(action: @escaping () -> Void) { self.action = action } @objc func handleTap() { action() } } } // Helper extension to copy UIBarButtonItem styles to a UIButton extension UIBarButtonItem { func configureButton(_ button: UIButton) { let normalAttrs = titleTextAttributes(for: .normal) button.titleLabel?.font = normalAttrs?[.font] as? UIFont button.setTitleColor(normalAttrs?[.foregroundColor] as? UIColor, for: .normal) let highlightedAttrs = titleTextAttributes(for: .highlighted) button.setTitleColor(highlightedAttrs?[.foregroundColor] as? UIColor, for: .highlighted) } }
Then use it in your SwiftUI view like this:
struct ContentView: View { var body: some View { NavigationStack { Text("Hello, SwiftUI!") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { SystemBarButton(title: "Done", style: .done) { // Your tap action here } } } } } }
This wrapper ensures your button inherits all of UIKit's style handling—dynamic type support, system theme changes, and future design updates included.
Option 3: iOS 16+ Simplified System Font Matching
If you only need to support iOS 16 and above, you can directly use the system's bar button font to mimic the Done style without wrapping UIKit:
struct ContentView: View { var body: some View { NavigationStack { Text("Hello, SwiftUI!") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Done") { // Handle tap } .font(UIFont.systemFont(ofSize: UIFont.buttonFontSize, weight: .semibold)) } } } } }
Note that this is a manual approximation, so it might not catch all system style changes (like subtle color adjustments) the way the UIKit wrapper does.
Key Takeaways
- Avoid hardcoding font weights or colors—system styles evolve, and manual settings will become outdated.
- Use the
toolbarmodifier instead ofnavigationBarItems—it's Apple's recommended API for modern SwiftUI navigation. - For full system compatibility across iOS versions, the UIKit wrapper is the most robust solution.
内容的提问来源于stack exchange,提问作者Edward Brey

