如何在SwiftUI的MenuBarExtra中实现带边框的特定Form样式?
我在SwiftUI的MenuBarExtra(窗口样式)里实现Form时,遇到了样式问题:期望表单带有边框,但实际显示的Form没有边框。以下是我最初的代码:
struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } MenuBarExtra("Internet Service", systemImage: "network") { InternetInfo() } .menuBarExtraStyle(.window) } } struct InternetInfo: View { @State var usage: Double = 230.0 @State var quota: Double = 60.0 var body: some View { Form { Gauge(value: usage, in: 0...1000) { Text("Used Bandwidth") } currentValueLabel: { Text("\(Int(usage)) GB").font(.caption) } Gauge(value: quota, in: 0...100) { Text("Disk Quota") } currentValueLabel: { Text("\(Int(quota)) GB").font(.caption) } } .gaugeStyle(.accessoryLinearCapacity) .formStyle(.grouped) } }
问题原因
在macOS的MenuBarExtra窗口中,.formStyle(.grouped)不会像常规WindowGroup里那样自动添加边框——这是因为MenuBarExtra的窗口容器样式和普通窗口不同,系统默认不会给这里的Form加上边框装饰。
解决方法
方法1:直接给Form添加自定义边框
最简单的方式是给Form加上border修饰符,配合padding调整间距,快速模拟预期效果:
struct InternetInfo: View { @State var usage: Double = 230.0 @State var quota: Double = 60.0 var body: some View { Form { Gauge(value: usage, in: 0...1000) { Text("已用带宽") } currentValueLabel: { Text("\(Int(usage)) GB").font(.caption) } Gauge(value: quota, in: 0...100) { Text("磁盘配额") } currentValueLabel: { Text("\(Int(quota)) GB").font(.caption) } } .gaugeStyle(.accessoryLinearCapacity) .formStyle(.grouped) .border(Color(.separator), width: 1) // 添加系统分隔线颜色的边框 .padding(8) // 边框与Form之间留间距 .frame(width: 300, height: 180) // 可选:固定窗口尺寸,布局更整齐 } }
方法2:使用原生NSTextField边框(更贴近系统样式)
如果想要完全匹配macOS原生的边框视觉效果,可以封装一个NSViewRepresentable的容器:
// 封装带边框的原生容器 struct BorderedContainer: NSViewRepresentable { func makeNSView(context: Context) -> NSTextField { let field = NSTextField() field.isBordered = true field.isEditable = false field.drawsBackground = true field.backgroundColor = .textBackgroundColor return field } func updateNSView(_ nsView: NSTextField, context: Context) {} } // 修改后的InternetInfo视图 struct InternetInfo: View { @State var usage: Double = 230.0 @State var quota: Double = 60.0 var body: some View { BorderedContainer() .padding(8) .overlay( Form { Gauge(value: usage, in: 0...1000) { Text("已用带宽") } currentValueLabel: { Text("\(Int(usage)) GB").font(.caption) } Gauge(value: quota, in: 0...100) { Text("磁盘配额") } currentValueLabel: { Text("\(Int(quota)) GB").font(.caption) } } .gaugeStyle(.accessoryLinearCapacity) .formStyle(.grouped) .padding(8) ) .frame(width: 300, height: 180) } }
第一种方法适合快速实现需求,第二种方法能获得更贴合系统的原生视觉体验,可以根据自己的需求选择。
内容的提问来源于stack exchange,提问作者MohAliBou
相关产品推荐
相关产品推荐

