i've been trying to make a good design for an app, however i've run into a problem. The design consists of 2 vstacks in 1 hstack and the problem occurs when a text view in a vstack wraps. this makes the text views that are under the vstack be unaligned. is there any way to make them always aligned? here is the relevant code.
this is file of a single vertical row, where the problem is located at, i presume
import SwiftUI
struct VerticalRowView: View {
var whichOfTheFour: Int
var movie: Movie
@Binding var detailScreenShown: Bool
var body: some View {
HStack(alignment: .firstTextBaseline) {
VStack(alignment: .leading, spacing: 25) {
if whichOfTheFour == 1 {
Text("Title:")
.onTapGesture {
detailScreenShown = true
}
.foregroundColor(.cyan)
Text("Genre:")
Text("Released:")
Text("Origin:")
Text("Rated:")
Text("Runtime:")
Text("Plot:")
}
else if whichOfTheFour == 2 {
Text("Director:")
Text("Actor:")
Text("Writer:")
Text("Languages:")
Text("Box office:")
}
else if whichOfTheFour == 3 {
Text("Awards:")
Text("Metascore:")
Text("IMDB Rating:")
Text("IMDB Votes:")
}
else if whichOfTheFour == 4 {
Text("DVD:")
Text("Production:")
}
}
VStack(alignment: .leading, spacing: 25) {
if whichOfTheFour == 1 {
Text(movie.title ?? "Unknown")
.onTapGesture {
detailScreenShown = true
}
.foregroundColor(.cyan)
Text(movie.genre ?? "Unknown")
Text(movie.released ?? "Unknown")
Text(movie.country ?? "Unknown")
Text(movie.rated ?? "Unknown")
Text(movie.runtime ?? "Unknown")
Text(movie.plot ?? "Unknown")
}
else if whichOfTheFour == 2 {
Text(movie.director ?? "Unknown")
Text(movie.actor ?? "Unknown")
Text(movie.writer ?? "Unknown")
Text(movie.language ?? "Unknown")
Text(movie.boxOffice ?? "Unknown")
}
else if whichOfTheFour == 3 {
Text(movie.awards ?? "Unknown")
Text(movie.metascore ?? "Unknown")
Text(movie.imdbRating ?? "Unknown")
Text(movie.imdbVotes ?? "Unknown")
}
else if whichOfTheFour == 4 {
Text(movie.DVD ?? "Unknown")
Text(movie.production ?? "Unknown")
}
}
}
}
}
this is the implementation
struct MovieDetailView: View {
@Binding var result: Movie
@Binding var detailScreenShown: Bool
var body: some View {
VStack() {
CustomDivider()
AsyncImage(url: URL(string: result.poster ?? ""))
CustomDivider()
VerticalRowView(whichOfTheFour: 1, movie: result, detailScreenShown: $detailScreenShown)
CustomDivider()
VerticalRowView(whichOfTheFour: 2, movie: result, detailScreenShown: $detailScreenShown)
CustomDivider()
VerticalRowView(whichOfTheFour: 3, movie: result, detailScreenShown: $detailScreenShown)
CustomDivider()
VerticalRowView(whichOfTheFour: 4, movie: result, detailScreenShown: $detailScreenShown)
}
}
}
ive been trying to solve this for over 6 hours, im still a newbie and i believe the solution is very silly.
As I said, I recommend using Table - it fits your use case like a glove, but if you have to replicate the same behavior without a Table, then I would do this:
VStack
s, have oneHStack
, using the GeometryReader
to ensure that all left and right columns align.Something like this:
First, the row view, with title and optional value, which we will resolve to Unknown
if it has no value:
struct MovieRowView: View {
let title: String
let value: String?
var body: some View {
GeometryReader { metrics in
HStack {
Text("\(title):")
.frame(
width: metrics.size.width * 0.4,
alignment: .topTrailing
)
Text(value ?? "Unknown")
.frame(
width: metrics.size.width * 0.6,
alignment: .topLeading
)
}
}
.frame(maxWidth: .infinity)
}
}
As you see, the GeometryReader
is used to ensure that every row will have exactly the same split between left and right column. In addition, we can align left column to the trailing
edge, and right column to leading
edge, so they are closer to each other. You can change any of the proportions / alignment based on your needs of course.
Next, lets put all the rows into VStack
:
struct MovieView: View {
// ...
var body: some View {
VStack {
MovieRowView(title: "Title", value: movie.title)
.onTapGesture {
detailScreenShown = true
}
.foregroundColor(.cyan)
MovieRowView(title: "Genre", value: movie.genre)
MovieRowView(title: "Released", value: movie.released)
// etc
}
}
}