swiftui matchedgeometryeffect is not smooth

345 views Asked by At

I have two views, one view has an HStack that displays a list of views, each containing an image and text. When one view is clicked, a detail view is displayed which will contain the image and text of the selected view.

The problem is the matchedgeometryeffect seems to fade in when transitioning between views. I tried different solution here on stackoverflow such as using .zIndex() and .transition(.scale(scale: 1)) but nothing worked for me.

Here is the behavior I get in simulator: video

Here is the code:

struct Test: View {
    var body: some View {
    
    VStack {
        if let selectedCard = selectedCard {
            VStack {
                cardView(card: selectedCard)
                    .onTapGesture {
                        withAnimation() {
                            self.selectedCard = nil
                        }
                    }
            }
        } else {
            ScrollView(.horizontal, showsIndicators: false) {
                HStack {
                    ForEach(topCardsAsTemplates) { topCardAsTemplate in
                        TopTemplatesCardView(card: topCardAsTemplate)
                            .padding()
                            .onTapGesture {
                                withAnimation() {
                                    selectedCard = topCardAsTemplate
                                }
                            }
                    }
                }
            }
        }
    }
}

@ViewBuilder
func TopTemplatesCardView(card: Card) -> some View {
    
    if card.uuid != selectedCard?.uuid {
        VStack {
            Image(systemName: "person.circle")
                .resizable()
                .matchedGeometryEffect(id: card.uuid.uuidString + "image", in: namespace)
                .scaledToFit()
                .frame(width: 80)
            
            Text(card.backgroundType.rawValue)
                .matchedGeometryEffect(id: card.uuid.uuidString + "text", in: namespace, properties: .position)
                .foregroundColor(.black)
 //                    .matchedGeometryEffect(id: card.uuid.uuidString + "text", in: namespace)
                                
        }
        .padding()
        .background(
            RoundedRectangle(cornerRadius: 20, style: .continuous)
                .matchedGeometryEffect(id: card.uuid.uuidString + "back", in: namespace)
                .frame(width: cardSize + 50, height: cardSize + 50)
                .background(.clear)
                .foregroundColor(.gray)
        )
        
    } else {
        
        Color.clear.frame(width: cardSize, height: cardSize)
    }
}

    @ViewBuilder
func cardView(card: Card) -> some View {
    
    VStack {
        Image(systemName: "person.circle")
            .resizable()
            .matchedGeometryEffect(id: card.uuid.uuidString + "image", in: namespace)
            .scaledToFit()
            .frame(width: 120)
        
        Text(card.backgroundType.rawValue)
            // .matchedGeometryEffect(id: card.uuid.uuidString + "text", in: namespace)
            .matchedGeometryEffect(id: card.uuid.uuidString + "text", in: namespace, properties: .position)
            .foregroundColor(.black)
    }
    .padding()
    .background(
        RoundedRectangle(cornerRadius: 20, style: .continuous)
            .matchedGeometryEffect(id: card.uuid.uuidString + "back", in: namespace)
            .frame(width: cardSize + 200 , height: cardSize + 200)
            .background(.clear)
            .foregroundColor(.gray)
    )
}
}

How can I make the transition smoother so that the fading-in and out are not shown? Any suggestions please?

Edit: After a lot of trial and error, adding .frame(maxWidth: .infinity, maxHeight: .infinity) to the HStack seems to solve the problem for me. Here is how the HStack looks like now:

ScrollView(.horizontal, showsIndicators: false) {
            HStack {
                ForEach(topCardsAsTemplates) { topCardAsTemplate in
                    TopTemplatesCardView(card: topCardAsTemplate)
                        .padding()
                        .onTapGesture {
                            print("display card uuid: \(topCardAsTemplate.uuid.uuidString)")
                            withAnimation(.spring()) {
                                selectedCard = topCardAsTemplate
                            }
                        }
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }

Hope this helps other people.

0

There are 0 answers