swiftuigeometryreaderlazyvgrid

How to reduce usage of geometry reader in a LazyVgrid?


In this code, is it a problem usage of geometryReader both in view and in cells? is there a different way to achieve same result?). to be clear if I have 5 cells, I had hard time keeping the image in the same position even if text is on single line. I need to avoid getting some cell with image close to top and other at a lower position

I'd like to keep these requirements:


Solution

  • If you don't need the 1:1 aspect ratio, you can just scaleToFit the image, and add a Spacer between the text and the image (or after the text, depending on what alignment you want):

    // ItemView's body:
    VStack {
        Image(systemName: "apple.logo")
            .resizable()
            .scaledToFit()
            .foregroundStyle(.secondary)
        Spacer()
        if item % 2 == 0 {
            Text("short: \(item)")
                .lineLimit(2)
                .minimumScaleFactor(0.8)
                .multilineTextAlignment(.center)
                .padding(.horizontal, 5)
                .padding(.top, 5)
        } else {
            Text("middle lenght text: \(item)")
                .lineLimit(2)
                .minimumScaleFactor(0.8)
                .multilineTextAlignment(.center)
                .padding(.horizontal, 5)
                .padding(.top, 5)
        }
    }
    .background(.teal)
    .shadow(color: .black.opacity(0.3), radius: 10, x: 0, y: 10)
    

    The spacer pushes the image and text apart from each other, making sure that the images always align at the top.

    enter image description here

    If you are not satisfied with this, then I suggest keeping the GeometryReader. Making the Text 1/4 of the height of the VStack is very reasonable.

    Otherwise, you can put an invisible 2-line text, and overlay the actual text on top of it. IMO, this is more of a hack when compared to GeometryReader.

    VStack{
        Image(systemName: "apple.logo")
            .resizable()
            .scaledToFit()
            .foregroundStyle(.secondary)
        // this takes up 2 lines
        Text("\n")
            .lineLimit(2)
            .minimumScaleFactor(0.8)
            .multilineTextAlignment(.center)
            .padding(.horizontal, 5)
            .padding(.top, 5)
            .frame(maxWidth: .infinity)
            .overlay { // specify the alignment here, if needed
                Group {
                    if item % 2 == 0 {
                        Text("short: \(item)")
                    } else {
                        Text("middle lenght text: \(item)")
                    }
                }
                .lineLimit(2)
                .minimumScaleFactor(0.8)
                .multilineTextAlignment(.center)
                .padding(.horizontal, 5)
                .padding(.top, 5)
            }
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .aspectRatio(1, contentMode: .fit)
    .background(.teal)
    .shadow(color: .black.opacity(0.3), radius: 10, x: 0, y: 10)
    

    enter image description here