SwiftUI 表单对齐 macOS

Jul*_*ian 2 swift swiftui

我试图HStack在 SwiftUI 中包含自定义行,Form如下所示:

var body: some View {
    Form {
        TextField("Text", text: .constant("test"))
        Toggle("Toggle", isOn: .constant(true))
            .toggleStyle(SwitchToggleStyle())
        HStack {
            Text("Label")
            MenuButton("Menu") {
                Button(action: {
                    print("Clicked Pizza")
                }) { Text("Pizza") }
                Button(action: {
                    print("Clicked Pasta")
                }) { Text("Pasta") }
            }
            TextField("Topping", text: .constant("Cheese"))
                .labelsHidden()
        }
        
    }
    .padding()
}
Run Code Online (Sandbox Code Playgroud)

导致

SwiftUI 表单输出对齐错误

但是,我希望与切换Label垂直对齐Toggle并垂直对齐。Menu

是否有选择自定义行对齐模式的标准方法HStack

Sco*_*man 5

仅限 macOS 13

SwiftUI 中的新LabeledContent视图将帮助您:

LabeledContent("Label") {
  MenuButton("Menu") {
    Button(action: {
      print("Clicked Pizza")
    }) { Text("Pizza") }
    Button(action: {
      print("Clicked Pasta")
    }) { Text("Pasta") }
  }
  TextField("Topping", text: .constant("Cheese"))
    .labelsHidden()
  }
}
Run Code Online (Sandbox Code Playgroud)

适用于 macOS 13 的 SwiftUI 中带有 LabeledContent 的示例布局

但是,此视图尚未向后移植到早期版本的 macOS,因此如果您需要支持早期版本,则需要另一种方法。


早期版本的 macOS

基于@Nhat Nguyen Duc 的首选项键代码构建,关键是使用对齐指南而不是填充。创建自定义视图,并使用仅测量宽度的自定义首选项:

struct LabeledHStack<Content: View>: View {
    var label: String
    var content: () -> Content
    @State var labelWidth: CGFloat = 0

    init(_ label: String, @ViewBuilder content: @escaping () -> Content) {
        self.label = label
        self.content = content
    }

    var body: some View {
        HStack {
            Text(label)
                .readSize { self.labelWidth = $0 }
            content()
        }
        .alignmentGuide(.leading) { _ in labelWidth + 10 } // see note
    }
}

struct WidthPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { }
}

extension View {
    func readWidth(onChange: @escaping (CGFloat) -> Void) -> some View {
        background(
            GeometryReader { geometryProxy in
                Color.clear
                    .preference(key: WidthPreferenceKey.self, value: geometryProxy.size.width)
            }
        )
        .onPreferenceChange(WidthPreferenceKey.self, perform: onChange)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在自定义视图中,我添加了 10 个像素来快速模拟标签与其表单元素之间的间距。可能有更好的方法来使其适用于可访问性大小等(例如,使用值@ScaledMetric)。您可能还希望将其应用为填充,而不是在对齐参考线计算中。

下面有一行 macOS13 的LabeledContent,后面跟着LabeledHStack

带 LabeledHStack 的表单