ScrollView text alignment SwiftUI

Viewed 459

I have a vertical ScrollView with a ForEach loop that has HStack's inside with Text, Image, and Two Text's. I'd like to keep each row's items in alignment with each other. I've attempted to wrap in a VStack with alignment .leading but did nothing. The issue I'm having is the text doesn't line up. Still very new to SwiftUI so any help would be appreciated.

Issue

ScrollView(.vertical) {
    ForEach(self.daily.forecast, id: \.date) { forecast in
        HStack (spacing : 10){
            Text(forecast.date ?? "")
                .fontWeight(.bold)
            Spacer()
            RequestImage(Url("IMAGE_URL"))
                .aspectRatio(contentMode: .fit)
                .frame(width: 30, height: 30)
                .clipped()
            Spacer()
            Text("\(forecast.maxTemp)")
                .fontWeight(.bold)
            Text("\(forecast.minTemp)")
                .fontWeight(.bold)
        }
    }
}
.padding(.all, 15)
.onAppear() {
    authorize {_ in
        loadForecast()
    }
}

UPDATED: I was able to get a bit closer but things still aren't aligned to where I'd like them to be.

ScrollView(.vertical) {
    ForEach(self.daily.forecast, id: \.date) { forecast in
        HStack (spacing : 10){
            Text(date)
                .font(.title3)
                .fontWeight(.bold)
                .frame(minWidth: 45)
            Spacer()
            RequestImage(Url("IMAGE_URL"))
                .aspectRatio(contentMode: .fit)
                .frame(width: 35, height: 35)
                .frame(minWidth: 50)
                .clipped()
            Spacer()
            Text(maxTemp)
                .font(.title3)
                .fontWeight(.bold)
                .frame(minWidth: 45)
            Text(minTemp)
                .font(.title3)
                .fontWeight(.bold)
                .foregroundColor(Color.secondary)
                .frame(minWidth: 45)
        }
        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
    }
}
.padding(.all, 15)
.onAppear() {
    authorize {_ in
        loadForecast()
    }
}

enter image description here

3 Answers

Here is a demo of possible solution. Prepared with Xcode 12.4 / iOS 14.4

demo

struct DemoWeatherLayout: View {
    private var dayOfTheWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sut"]
    var body: some View {
        ScrollView {
            VStack {
                ForEach(dayOfTheWeek, id: \.self) {
                    WeatherRowView(day: $0, 
                       icon: ["sun.max", "cloud.sun", "cloud", "cloud.snow"].randomElement()!,
                       low: Array(-5..<0).randomElement()!, 
                       high: Array(1..<15).randomElement()!)
                }
            }.padding()
        }
    }
}

struct WeatherRowView: View {
    var day: String
    var icon: String
    var low: Int
    var high: Int

    var body: some View {
        HStack {
            Text(day)
                .frame(maxWidth: .infinity, alignment: .leading)
            Image(systemName: icon)
                .frame(maxWidth: .infinity, alignment: .leading)
            HStack {
                Text("+XXº").foregroundColor(.clear)
                    .overlay(Text(String(format: "%+d", low)), alignment: .leading)
                Text("+XXº").foregroundColor(.clear)
                    .overlay(Text(String(format: "%+d", high)), alignment: .leading)
            }
        }
    }
}

Three suggestions:

  • add alignment parameter to HStack:

    HStack(alignment: .lastTextBaseline, spacing: 10) { ... //others options: .top, .center, .firstTextBaseline...

  • Add minLength parameter to Spacer:

    Spacer(minLength: 0) but with spacing: 10 in HStack it is a bit redundant. You can remove spacing: 10 for Spacer(minLength: 10)

  • Add a Spacer(minLength: 0) at the end after Text("\(forecast.minTemp)").fontWeight(.bold)

I'm going to accept @Asperi answer as correct. The UI has indeed changed since to better fit said labels etc. Appreciate everyone's else. The code is very much ugly but works. Not sure if it's 100% correct.

HStack (spacing: 10){
    Text(date)
        .font(.title3)
        .fontWeight(.bold)
        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
    Spacer()
    RequestImage(Url("IMAGE_URL_HERE"))
        .aspectRatio(contentMode: .fit)
        .frame(width: 35, height: 35)
        .frame(minWidth: 55)
        .clipped()
    Spacer()
    Text(maxTemp)
        .font(.title3)
        .fontWeight(.bold)
        .frame(minWidth: 50)
        .background(Color.orange)
        .cornerRadius(5)
    Text(minTemp)
        .font(.title3)
        .fontWeight(.bold)
        .frame(minWidth: 50)
        .background(Color.blue)
        .cornerRadius(5)
}

enter image description here

Related