SwiftUI transition(.asymmetric..) not working with Offset

Viewed 309

I am working on a Set Game and I want the cards to swing on the view (from off the screen) via the .transition(.asymmetric(insertion: .offset(x: 100.0, y: 100.0), removal: .offset(x: 100.0, y: 100.0))) command.

Actually, the cards leave the screen by doing exactly the offset, but they just 'appear' at the start on the program rather than actually coming off the screen..

Video of the App

As you can see the Cards that are selected and 'matched' fly off the screen with the .transition(offset). I would like to see the cards fly in at the beginning of the Game from off the screen in the same manner.

@ViewBuilder
private func body (for size: CGSize) -> some View{
    let paddingAllSidesInCards:CGFloat = 8
    if card.isFaceUp || !card.isMatched
    {
        GeometryReader { geo in
            VStack(alignment: .center) {
                Group
                {
                        
                        ForEach( 0..<card.numberofsymbols)
                        { numberofcards in
                            
                            let cardshape = cardViewModelShape.getShape(Card: card, size: size, numberOfCards: card.numberofsymbols)
                                cardshape
                                .transition(.identity)
                                .aspectRatio(1,contentMode: .fit)
                                .frame(minWidth: 20, idealWidth: 100, maxWidth: .infinity, minHeight: 20, idealHeight: 100, maxHeight: .infinity, alignment: .center)
                                .scaleEffect(card.isSelected ? 1.2 : 1)
                                .animation(.spring(response: (card.isSelected ? 1:0), dampingFraction: 0.1, blendDuration: .infinity))
                        }.padding(paddingAllSidesInCards)
                    //Implicit animation - with linear execution
                }
            }
            .cardify(isFaceUp: card.isFaceUp)
        }
        .transition(.asymmetric(insertion: .offset(x: 100.0, y: 100.0), removal: .offset(x: 100.0, y: 100.0)))
   

    }
}

I am doing something wrong or forgetting a point - I tried the onAppear() operator and it did not work.

Thanks for your help!

PS: If you want to see the full source code, you can check out the repository:

Link SetGame Repo

2 Answers

Provided code is not testable, so just an idea - try to wrap everything into container (it is needed as a placeholder to animate transitions), like

@ViewBuilder
private func body (for size: CGSize) -> some View{
    let paddingAllSidesInCards:CGFloat = 8

    VStack // << this one !!
    {
      if card.isFaceUp || !card.isMatched
      {
        GeometryReader { geo in
            VStack(alignment: .center) {

               // .. other code
            }
            .cardify(isFaceUp: card.isFaceUp)
        }
        .transition(.asymmetric(insertion: .offset(x: 100.0, y: 100.0), removal: .offset(x: 100.0, y: 100.0)))
      }
   }
   .animation(.default) // << use any you want
}

As always the solution was near by - I have placed my animation and transition at the wrong place --> I have tried to place it for each individual card, but actually, I wanted every card at the same time glide in view.

There are different ways to do it and I decided to use a state and an animated offset, so that the cards glide into the view.

struct SetGameView: View {
@ObservedObject var viewModel: SetGameViewModel
@State private var offset = 1000
//var card: SetGameModel<String>.Card
var body: some View {
    VStack()
    {
        Grid(viewModel.cards) { card in
            Cardview(cardViewModelShape: viewModel, card: card).onTapGesture
            {
                withAnimation(.linear(duration: self.AnimationDuration))
                {
                    self.viewModel.choose(card: card)
                }
            }
            
        }.offset(x: 0.0, y: CGFloat(offset)) // That's important
}
        
Related