I'm trying to achieve the following 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:
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:
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)
}
}
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)
}
}