This should be a simple thing I am missing so I apologize in advance. I have reusable view "TextRowView" that has 2 grid columns one will be a fixed width and the other I would like to take up all available space.
I don't understand why It displays correctly in the TruckView Preview but doesn't display correctly in the list view
struct TextRowView: View {
let columns = [
GridItem(.fixed(140), alignment: .leading),
GridItem(.flexible(), alignment: .leading)
]
let title: String
let value: String
var body: some View {
LazyVGrid(columns: columns) {
Text(title)
.bold()
Text(value)
}
}
}
Here is the cell for the list view
import SwiftUI
struct TruckView: View {
let truck: Truck
var body: some View {
VStack {
TextRowView(title: "Year:", value: truck.year)
TextRowView(title: "Make:", value: truck.make)
TextRowView(title: "Model:", value: truck.model)
TextRowView(title: "Driver:", value: truck.driver)
TextRowView(title: "Truck #:", value: truck.truckNumber.formatted(.number.grouping(.never)))
}
.foregroundStyle(Color(hex: truck.foregroundColor))
.padding()
.background(RoundedRectangle(cornerRadius: 10)
.fill(Color(hex: truck.backgroundColor)))
}
}
here is the list view
import SwiftUI
struct TripListView: View {
@Environment(AppController.self) private var appController
var body: some View {
Group {
List {
ForEach(appController.filteredTrucks) { truck in
NavigationLink {
} label: {
TruckView(truck: truck)
}
}
}
}
}
}
Here is what it looks like in the simulator.
and this is the TruckView in the preview.
This appears to work but I am not sure if it is the correct / best way of handling this situation.
struct TextRowView: View {
let columns = [
GridItem(.fixed(140), alignment: .leading),
GridItem(.flexible(minimum: UIScreen.main.bounds.width - 210), alignment: .leading)
]
let title: String
let value: String
var body: some View {
LazyVGrid(columns: columns) {
Text(title)
.bold()
Text(value)
}
}
}
UPDATE: I was using this and was told a Grid would be better.
struct TextRowView: View {
let title: String
let value: String
let width: CGFloat
var body: some View {
ViewThatFits(in: .horizontal) {
LabeledContent {
Text(value)
.frame(width: width, alignment: .leading)
} label: {
Text(title)
.bold()
}
VStack {
Text(title)
.bold()
Text(value)
.frame(width: width, alignment: .leading)
}
}
}
}
Since you are only displaying one row of details and the width of the first cell is fixed, it would be simpler to use an HStack
in TextRowView
instead:
// TextRowView
HStack {
Text(title)
.bold()
.frame(maxWidth: 140, alignment: .leading)
Text(value)
.frame(maxWidth: .infinity, alignment: .leading)
}
Using a Grid
only makes sense if there is more than one row. So if you really want to use a grid, I would suggest the following changes:
In TextRowView
, remove the parent container. This way, the body
function returns two "loose" views. This works, because View.body
is implicitly a ViewBuilder
.
Move the definition of columns
from TextRowView
to TruckView
.
In TruckView
, change the VStack
to LazyVGrid
and supply the columns that came from TextRowView
.
Since the grid is shown in a List
row, apply a frame with maxWidth: .infinity
, so that it fills the full width of the row.
Here are the updated versions of these two views:
struct TextRowView: View {
let title: String
let value: String
var body: some View {
Text(title)
.bold()
Text(value)
}
}
struct TruckView: View {
let columns = [ // 👈 moved from TextRowView
GridItem(.fixed(140), alignment: .leading),
GridItem(.flexible(), alignment: .leading)
]
let truck: Truck
var body: some View {
LazyVGrid(columns: columns) { // 👈 changed
TextRowView(title: "Year:", value: truck.year)
TextRowView(title: "Make:", value: truck.make)
TextRowView(title: "Model:", value: truck.model)
TextRowView(title: "Driver:", value: truck.driver)
TextRowView(title: "Truck #:", value: truck.truckNumber.formatted(.number.grouping(.never)))
}
.frame(maxWidth: .infinity) // 👈 added
.foregroundStyle(Color(hex: truck.foregroundColor))
.padding()
.background(RoundedRectangle(cornerRadius: 10)
.fill(Color(hex: truck.backgroundColor)))
}
}
Since TextRowView
is now so trivial, you could replace it with a function in TruckView
instead. The function needs to be annotated with @ViewBuilder
:
@ViewBuilder
private func gridRow(title: String, value: String) -> some View {
Text(title)
.bold()
Text(value)
}
LazyVGrid(columns: columns) {
gridRow(title: "Year:", value: truck.year)
// ... etc.
}