Home > Software engineering >  How to align with a certain view in a VStack?
How to align with a certain view in a VStack?

Time:04-11

I'm trying to achieve the following layout:

Expected Layout

As you can see, Foo is aligned with Bar horizontally, and there's a vertical line at the bottom of Foo.

I haven't managed to do it, the best I can do is the following one:

My Layout

Here is the corresponding code:

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)
    }

}

My question is: how to modify the code to achieve the expected layout?

P.S. In the end, I want to achieve something like this:

Final Expectation

CodePudding user response:

Here is the complete code that you want. You need to use multiplier for constant so that it fits in all devices.

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)
    }
}

enter image description here

CodePudding user response:

Each of your activities are a single HStack, with the time on the left. The line is just something that connects each row, not necessarily part of the row itself.

You can use a ZStack to show the gray line on the back and the rows on the front; a background on the first column will avoid overlapping the gray line.

The alignment is assured by giving the same frame width to the first column (time) and the vertical line.

Here's an example:

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)
    }
}

enter image description here

  • Related