soleil
soleil

Reputation: 13085

Unexpected layout issues with SwiftUI Image and ZStack

I'm trying to create a view composed of two parts - the top part is an image with a piece of text overlaid in the top left corner. The top part should take up 2/3 of the height of the view. The bottom part is text information and a button, contained in an HStack, and takes up the remaining 1/3 of the view height.

struct MainView: View {
    var body: some View {
        GeometryReader { gr in
            VStack(spacing: 0) {
                ImageInfoView()
                    .frame(height: gr.size.height * 0.66)
                BottomInfoView()
            }
            .clipShape(RoundedRectangle(cornerRadius: 20))
        }
    }
}

struct ImageInfoView: View {

    var body: some View {
        ZStack(alignment: .topLeading) {
            Image("testImage")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .overlay(Rectangle()
                    .fill(Color.black.opacity(0.5))
                )
            
            HStack {
                Text("Top left text")
                    .foregroundColor(.white)
                    .padding()
            }
        }
    }
}

struct BottomInfoView: View {

    var body: some View {
        HStack {
            VStack(alignment:.leading) {
                Text("Some title")
                Text("Some subtitle")
            }
            
            Spacer()
            Button("BUTTON") {
            }
        }
        .padding()
        .background(.gray)
    }
}

In the below sample code, if I set the frame height to 400 I will see the top left text, but when I set it to 200, I do not. What is happening here? The text should be anchored to the top left corner of ImageInfoView no matter what. Further, when set to 400, why does the ImageInfoView take up more than 66% of the height?

struct TestView: View {
    var body: some View {
        MainView()
            .frame(height: 200)
            .padding([.leading, .trailing], 16)
    }
}

height 400 height 200

Upvotes: 2

Views: 1416

Answers (2)

soleil
soleil

Reputation: 13085

This fixes the problem:

ZStack(alignment: .topLeading) {
    GeometryReader { gr in
         Image("testImage")
             .resizable()
             .aspectRatio(contentMode: .fill)
             .frame(maxWidth: gr.size.width, maxHeight: gr.size.height)
             .overlay(Rectangle()
                  .fill(Color.black.opacity(0.5))
             )
                    
         HStack {
             Text("Top left text")
                 .foregroundColor(.white)
                 .padding()
             }
         }
     }
}

Upvotes: 0

Asperi
Asperi

Reputation: 258097

An image's fill pushes ZStack boundaries, so Text goes off screen (see this for details).

A possible solution is to put image into overlay of other container so it respects provided space instead of imposing own.

Tested with Xcode 13.4 / iOS 15.5

demo

    ZStack(alignment: .topLeading) {
        Color.clear.overlay(    // << here !!
            Image("testImage")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .overlay(Rectangle()
                    .fill(Color.black.opacity(0.5))
                )
        )
        // ... other code as-is

Upvotes: 4

Related Questions