TopTrailing alignment is not working when trying to position the vStack inside of zStack

41 Views Asked by At

I am new to SwiftUI. I am trying to create a view like heart card but not being able to place the vStack of heart and letter at the top-right(topTrailing) corner of the rectangle.

var body: some View {
    ZStack {
        RoundedRectangle(cornerRadius: 5.0)
            .fill(.white)
            .stroke(.gray, style: StrokeStyle(lineWidth: 2.0))
            .frame(width: 100, height: 150)
            .shadow(color: Color(.black).opacity(0.3), radius: 5)
        
        VStack(spacing: 0) {
            Text("A")
                .font(.callout)
            
            Image(systemName: "heart.fill")
                .resizable()
                .frame(width: 10, height: 10)
        }.frame(alignment: .topTrailing)
    }
}

here is what I get as output. enter image description here

Can someone please help.

2

There are 2 best solutions below

2
Benzy Neez On BEST ANSWER

If you add a border to the VStack, you can see how the content of the VStack completely fills its frame:

VStack(spacing: 0) {
    // as before
}
.frame(alignment: .topTrailing)
.border(.orange)

Screenshot

What this .frame modifier is doing is to align the content of the VStack within a frame of the same size. But since the content already fills the frame, it makes no difference.

With these changes it can be made to work:

  • Move the .frame modifier that sets the size of the card from the RoundedRectangle to the ZStack that contains all the content.

  • Add maxWidth: .infinity, maxHeight: .infinity to the .frame modifier being set on the VStack, so that the frame expands to use all of the space available.

  • You might like to add a bit of padding to the VStack too.

ZStack {
    RoundedRectangle(cornerRadius: 5.0)
        // as before, but without .frame

    VStack(spacing: 0) {
        // as before
    }
    .padding(8) // <- ADDED
    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) // <- maxWidth and maxHeight added
}
.frame(width: 100, height: 150) // <- moved from RoundedRectangle

So what this does is to fix the size of the ZStack to the card size. A RoundedRectangle (like any Shape) is greedy, so it expands to fill the same space. The .frame modifier on the VStack now causes it to expand to the size of the ZStack and the alignment parameter aligns the content as you intended it to.

Screenshow or with a border on the VStack: enter image description here

0
son On

If you want to tell ZStack to align its sub-views, you have to use ZStack alignment property, something like:

ZStack(alignment: topLeading) {
  ...
}

You could try this alternative way:

var body: some View {
    RoundedRectangle(cornerRadius: 5.0)
        .fill(.white)
        .stroke(.gray, style: StrokeStyle(lineWidth: 2.0))
        .frame(width: 100, height: 150)
        .shadow(color: Color(.black).opacity(0.3), radius: 5)
        .overlay(alignment: .topLeading) {
            VStack(spacing: 0) {
                Text("A")
                    .font(.callout)
                Image(systemName: "heart.fill")
                    .resizable()
                    .frame(width: 10, height: 10)
            }
            .padding(5)
        }
        .overlay(alignment: .bottomTrailing) {
            VStack(spacing: 0) {
                Text("A")
                    .font(.callout)
                Image(systemName: "heart.fill")
                    .resizable()
                    .frame(width: 10, height: 10)
            }
            .scaleEffect(CGSize(width: 1, height: -1)) //<- make the view up side down
            .padding(5)
        }
}

Output:

enter image description here