SwiftUI中具有水平和垂直对齐的布局

Jer*_*rry 7 alignment swiftui

我正在尝试完成此布局

所需的布局

如果我尝试将HStack包裹在VStack中,则会得到以下信息:

VStack中的HStack

如果我尝试将VStack包裹在HStack中,则会得到以下信息:

HStack中的VStack

有没有一种方法可以使文本与文本字段对齐,并从最长的标签到对齐的文本字段的开始获取标准间距?

Dam*_*aux 9

SwiftUI 网格来救援!

从 iOS 16 开始,您应该使用网格来实现此目的。

struct ContentView: View {
    let labels = ["Username", "Email", "Password"]

    var body: some View {
        Grid {
            ForEach(labels, id: \.self) { label in
                GridRow {
                    Text(label)
                    TextField(label, text: .constant(""))
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

iOS 13-15 遗留黑客攻击

您可以使用kontiki几何读取器 hack来实现此目的:

SwiftUI 实时预览

struct Column: View {
    @State private var height: CGFloat = 0
    @State var text = ""
    let spacing: CGFloat = 8
    
    var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: spacing) {
                Group {
                    Text("Hello world")
                    Text("Hello Two")
                    Text("Hello Three")
                }.frame(height: height)
            }.fixedSize(horizontal: true, vertical: false)
            VStack(spacing: spacing) {
                TextField("label", text: $text).bindHeight(to: $height)
                TextField("label 2", text: $text)
                TextField("label 3", text: $text)
            }.textFieldStyle(RoundedBorderTextFieldStyle())
        }.fixedSize().padding()
    }
}

extension View {
    func bindHeight(to binding: Binding<CGFloat>) -> some View {
        func spacer(with geometry: GeometryProxy) -> some View {
            DispatchQueue.main.async { binding.value = geometry.size.height }
            return Spacer()
        }
        return background(GeometryReader(content: spacer))
    }
}
Run Code Online (Sandbox Code Playgroud)

我们在这里仅读取第一个 TextField 的高度,并将其应用到三个不同的 Text View 上三次,假设所有 TextField 具有相同的高度。如果您的三个文本字段具有不同的高度或具有影响各个高度的出现/消失的验证标签,您可以使用相同的技术,但使用三个不同的高度绑定。

为什么这有点黑客行为?

因为此解决方案将始终首先渲染没有标签的 TextFields。在此渲染阶段,它将设置文本标签的高度并触发另一个渲染。在一个布局阶段渲染所有内容会更理想。


tet*_*chy 9

不是这里的专家,但我设法通过(1)选择2 VStacks-in-a- HStack替代方案,(2)构筑外部标签,(3)通过分配来使其摆脱默认的垂直扩展约束,从而实现了所需的布局他们的maxHeight = .infinity(4)固定HStack

struct ContentView: View {
    @State var text = ""
    let labels = ["Username", "Email", "Password"]

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                ForEach(labels, id: \.self) { label in
                    Text(label)
                        .frame(maxHeight: .infinity)
                        .padding(.bottom, 4)
                }
            }

            VStack {
                ForEach(labels, id: \.self) { label in
                    TextField(label, text: self.$text)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
            }
            .padding(.leading)
        }
        .padding(.horizontal)
        .fixedSize(horizontal: false, vertical: true)
    }
}
Run Code Online (Sandbox Code Playgroud)

这是结果预览:

在此处输入图片说明

为了解决外部标签和内部标签的基线未对齐的问题(与该特定布局无关的附带问题,例如,请参见本讨论),我手动添加了填充

学分本网站的启发我的道路上理解SwiftUI布局trickeries

  • 这个解决方案看起来不错。唯一的“黑客”是使文本与文本字段对齐的填充。 (2认同)