Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aligning VStack and HStack correctly

Tags:

swiftui

I can't figure out how to align the content of this view:

I tried many different approaches:

  • Fixed sized vstack
  • setting max and min width
  • setting the size of the images, etc.

Nothing seems to work :(

import SwiftUI

struct Welcome: View {
    var body: some View {
        VStack {
            Text("Welcome to XXX")
                .font(.title).bold()

            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                    .imageScale(.large)
                    .padding()
                VStack(alignment: .leading) {
                    Text("Lorem ipsum dolor")
                        .font(.headline)
                    Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore")
                        .font(.subheadline)
                        .foregroundColor(Color(.lightText))
                }
            }.padding()

            HStack {
                Image(systemName: "command")
                    .foregroundColor(Color(.systemOrange))
                    .imageScale(.large)
                    .padding()
                VStack(alignment: .leading) {
                    Text("Lorem ipsum dolor")
                        .font(.headline)
                    Text("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.")
                        .font(.subheadline)
                        .foregroundColor(Color(.lightText))
                }
            }.padding()

            HStack {
                Image(systemName: "cube")
                    .foregroundColor(Color(.systemOrange))
                    .imageScale(.large)
                    .padding()
                VStack(alignment: .leading) {
                    Text("At vero eos et accusamus")
                        .font(.headline)
                    Text("At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque ")
                        .font(.subheadline)
                        .foregroundColor(Color(.lightText))
                }
            }.padding()
                Button(action: {
                    print("skip")
                }) {
                    HStack {
                        Spacer()
                        Text("Skip")
                            .font(.subheadline)
                        Spacer()
                    }
                    .foregroundColor(Color(.systemOrange))
                }.padding(.top, 60)

                Button(action: {
                    print("continue")
                }) {
                    HStack {
                        Spacer()
                        Text("Sign In")
                            .font(.subheadline)
                        Spacer()
                    }
                    .padding(12)
                    .foregroundColor(Color(.label))
                    .background(Color(.systemOrange))
                    .cornerRadius(12)
                }.padding()
            }
    }
}

It's quite annoying that the Human Interface Guidelines recommend creating welcome screens such as this and they don't provide a simple to do it.

enter image description here

like image 663
Mycroft Canner Avatar asked Oct 14 '19 12:10

Mycroft Canner


People also ask

What is VStack and HStack?

HStack positions views in a horizontal line, VStack positions them in a vertical line, and ZStack overlays views on top of one another.

What is VStack and HStack in SwiftUI?

You can use 3 kinds of stacks with SwiftUI: VStack, a vertical stack, which shows views in a top-to-bottom list. HStack, a horizontal stack, which shows views in a left-to-right list. ZStack, a depth-based stack, which shows views in a back-to-front list.

How do I move VStack?

You just need to add Spacer() & embed your view in VStack & HStack to force push the view to the top. Show activity on this post. You can remove the HStack and place the Spacer() within your VStack which will force the view to the top.

How do I align text in SwiftUI?

To align a text view along the horizontal axis, you need to use . frame() modifier with maxWidth set to . infinity and alignment to the alignment you want.


2 Answers

Just to get a little deeper on this issue (please, consider that the @Mycroft's answer is correct): In order to get the UI shown in the question the Spacer is necessary, it's not optional. I created a minimum viable example to show exactly what would happen without the Spacer view:

struct ContentView: View {
    var body: some View {
        VStack {
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                VStack(alignment: .leading) {
                    Text("Sed ut perspiciatis unde omnis iste natus")
                }
            }
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                VStack(alignment: .leading) {
                    Text("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.")
                }
            }
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                VStack(alignment: .leading) {
                    Text("At vero eos ")
                }
            }
        }
    }
}

The result is:

enter image description here

As you can see the result is completely misaligned. So, you must add the Spacer view in order to get the alignment you want.

struct ContentView: View {
    var body: some View {
        VStack {
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                VStack(alignment: .leading) {
                    Text("Sed ut perspiciatis unde omnis iste natus")
                }
                Spacer()
            }
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                VStack(alignment: .leading) {
                    Text("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.")
                }
                Spacer()
            }
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color(.systemOrange))
                VStack(alignment: .leading) {
                    Text("At vero eos ")
                }
                Spacer()
            }
        }
    }
}

enter image description here

In the layout above you have "two columns", the left one with the images and the right one with the texts. If you don't specify any size, the layout system will position the images and then it will give the remaining space to the texts. But there's another option if you need to specify specific sizes: if you need the image left column to be, for example, half of the screen you can use GeometryReader:

struct ContentView: View {
    var body: some View {
        GeometryReader { g in
            VStack {
                HStack {
                    HStack {
                        Spacer()
                        Image(systemName: "magnifyingglass")
                            .foregroundColor(Color(.systemOrange))
                    }
                    .frame(width: g.size.width/2.0)

                    VStack(alignment: .leading) {
                        Text("Sed ut perspiciatis unde omnis iste natus")
                    }
                    Spacer()
                }
                HStack {
                    HStack {
                        Spacer()
                        Image(systemName: "magnifyingglass")
                            .foregroundColor(Color(.systemOrange))
                    }
                    .frame(width: g.size.width/2.0)

                    VStack(alignment: .leading) {
                        Text("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Sed ut ")
                    }
                    Spacer()
                }
                HStack {
                    HStack {
                        Spacer()
                        Image(systemName: "magnifyingglass")
                            .foregroundColor(Color(.systemOrange))
                    }
                    .frame(width: g.size.width/2.0)
                    VStack(alignment: .leading) {
                        Text("At vero eos ")
                    }
                    Spacer()
                }
            }
        }
    }
}

enter image description here

like image 142
matteopuc Avatar answered Nov 06 '22 14:11

matteopuc


Adding a spacer seems to do the trick :

            HStack {
                Image(systemName: "cube")
                    .foregroundColor(Color(.systemOrange))
                    .imageScale(.large)
                    .padding()
                VStack(alignment: .leading) {
                    Text("At vero eos et accusamus")
                        .font(.headline)
                    Text("At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque ")
                        .font(.subheadline)
                        .foregroundColor(Color(.lightText))
                }
                Spacer()
            }.padding()
like image 35
Mycroft Canner Avatar answered Nov 06 '22 14:11

Mycroft Canner