Search code examples
swiftuialignment

How to make a ScrollView align to left side of frame?


For the sake of practicing, am trying to make a Worlde type app, nothing original.

I would like to make it so that the ScrollView, the list of words at the top of the VStack, is aligned to the left side of the screen. Even tried embedding it its own VStack with an alignment of .leading, but obviously to no avail.

Is there any alignment option I can use, or must I go to GeometeryReader for something like this?

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var word : Word
    
    var body: some View {
        ZStack {
            VStack {
                VStack(alignment: .leading) {
                    ScrollView{
                        if word.guessedWords.count > 0 {
                            
                            ForEach(0..<word.guessedWords.count, id: \.self) { count in
                                HStack {
                                    Text(word.guessedWords[count])
                                        .foregroundColor(.white)
                                        .padding(.trailing, 10)
                                    Text("\(count)")
                                        .foregroundColor(.white)
                                }
                            }
                        }
                    }
                    .padding(.top, 75)
                }
                
                Spacer()
                
                GuessedLetters()
                
                Keyboard()
                    .padding(.bottom)
                
            }
        }
        .background(.black)
        .ignoresSafeArea()
    }
    
}

#Preview {
    ContentView()
        .environmentObject(Word())
}

enter image description here


Solution

  • The alignment given to the VStack determines how multiple views inside the VStack are aligned to each other, when they have different widths. However, if the VStack only contains one subview (this being the ScrollView), it has no effect.

    Here are three possible ways to fix:

    1. Set a frame with maxWidth: .infinity and alignment: .leading on the ScrollView

    ScrollView {
        // ...
    }
    .padding(.top, 75)
    .frame(maxWidth: .infinity, alignment: .leading) // <- ADDED
    

    This will push the contents of the ScrollView to the left, but the rows themselves will not be left aligned.

    2. Add a Spacer to the HStack inside the ScrollView

    This pushes the content to the left and the content will also be left aligned:

    ScrollView {
        // ...
                HStack {
                    Text(word.guessedWords[count])
                        .foregroundColor(.white)
                        .padding(.trailing, 10)
                    Text("\(count)")
                        .foregroundColor(.white)
                    Spacer() // <- ADDED
                }
         // ...
    }
    

    3. Use a nested VStack inside the ScrollView

    This is where alignment: .leading will now be useful. You can also push it all to the left side using maxWidth: .infinity, as for variant 1:

    ScrollView {
        if word.guessedWords.count > 0 {
            VStack(alignment: .leading) {
                ForEach(0..<word.guessedWords.count, id: \.self) { count in
                    // ...
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)
        }
    }