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

SwiftUI中如何设置导航栏项样式?适配系统样式变化

How to Set Navigation Bar Item Styles in SwiftUI (Matching UIKit's UIBarButtonItem.style)

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 toolbar modifier instead of navigationBarItems—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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:22:31