如何与 VStack 中的某个视图对齐?

How to align with a certain view in a VStack?

我正在尝试实现以下布局:

可以看到,FooBar水平对齐,Foo下方有一条竖线。

我还没有做到,我能做的最好的是以下一个:

对应代码如下:

import SwiftUI

struct ContentView: View {

    var body: some View {
        HStack {
            VStack(spacing: 5) {
                Text("Foo")
                    .padding(.bottom, 0)
                getVerticalLine()
            }
            Text("Bar")
                .padding(10)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(.yellow)
        }
        
    }
    
    private func getVerticalLine() -> some View {
        return Color.gray
            .frame(width: 1, height: 30)
            .padding(.leading, 0)
    }

}

我的问题是:如何修改代码达到预期的布局?

P.S。最后,我想实现这样的目标:

这是您想要的完整代码。您需要对常量使用乘数,以便它适合所有设备。

CententView

struct ContentView: View {

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            FooBarView(isLastOne: false)
                .padding(EdgeInsets(top: 0, leading: 10, bottom: -18, trailing: 0))
            FooBarView(isLastOne: false)
                .padding(EdgeInsets(top: 0, leading: 10, bottom: -18, trailing: 0))
            FooBarView(isLastOne: false)
                .padding(EdgeInsets(top: 0, leading: 10, bottom: -18, trailing: 0))
            FooBarView(isLastOne: true)
                .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0))
        }
    }
}

FooBarView

struct FooBarView: View {
    var isLastOne: Bool
    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            HStack {
                VStack(spacing: 5) {
                    Text("Foo")
                        .padding(.bottom, 0)
                }
                Text("Bar")
                    .padding(10)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(.yellow)
            }
            if !isLastOne {
                getVerticalLine()
                    .padding(EdgeInsets(top: -5, leading: 12, bottom: 0, trailing: 0))
            }
        }
    }
    
    private func getVerticalLine() -> some View {
        return Color.gray
            .frame(width: 1, height: 40)
            .padding(.leading, 0)
    }
}

您的每个活动都是一个 HStack,时间在左侧。线只是连接每一行的东西,不一定是行本身的一部分。

您可以使用ZStack来显示背面的灰线和正面的行;第一列的背景将避免与灰线重叠。

通过为第一列(时间)和垂直线提供相同的帧宽度来确保对齐。

这是一个例子:

struct Example: View {
    
    struct Activity: Hashable, Identifiable {
        let id = UUID()
        let time: String
        let activity: String
    }
    
    let activities = [Activity(time: "10.00", activity: "Exercise"),
                      Activity(time: "11.00", activity: "Work"),
                      Activity(time: "12.00", activity: "Study"),
                      Activity(time: "13.00", activity: "Coding")]
    
    let firstColumnWidth = 80.0
    let spacingBetweenLines = 30.0
    let internalPadding = 10.0
    let lineHeight = 20.0

    var body: some View {
        
        ZStack {
            HStack {
                getVerticalLine
                    .frame(width: firstColumnWidth)
                Spacer()
            }
            
            VStack(spacing: spacingBetweenLines) {
                ForEach(activities) { item in
                    HStack(spacing: 5) {
                        Text(item.time)
                            .frame(height: lineHeight)
                            .padding(internalPadding)
                            .frame(width: firstColumnWidth)
                            .background(.white)
                        Text(item.activity)
                            .frame(height: lineHeight)
                            .padding(internalPadding)
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .background(.yellow)
                    }
                }
            }
        }
        .padding()
    }
    
    private var verticalLine: some View {
        let lineHeight = internalPadding * 2 + lineHeight + spacingBetweenLines
        return Color.gray
            .frame(width: 1, height: lineHeight * Double(activities.count - 1))
            .background(.green)
    }
}