How to move cards down separately inside ZStack?

Viewed 51

I have a card deck inside ZStack and I would like to move cards down one by one. I am trying to achieve that when the moving card hit the bottom, the upcoming card should start moving down. The problem I faced is that the whole ZStack is moving down instead of one card.

import SwiftUI

struct Card: Identifiable {
    
    let id = UUID()
    let color: Color
    let name: String
}

struct CardView: View {
    
    let card: Card
        
    var body: some View {
        Rectangle()
            .fill(card.color)
            .frame(width: 120, height: 120)
            .cornerRadius(10)
            .overlay(
                Text(card.name)
                    .font(.headline)
                    .foregroundColor(.white)
            )
    }
}

struct ContentView: View {
    var cards: [Card] = [
        Card(color: Color.red, name: "First Card"),
        Card(color: Color.green, name: "Second Card"),
        Card(color: Color.orange, name: "Third Card"),
    ]
    
    @State var timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
    @State var movingDownSpeed = CGPoint(x: 0, y:10)
    @State var cardPosition = CGPoint(x: UIScreen.main.bounds.size.width * 0.5, y:75)

 
    var body: some View {
        
        ZStack {
            ForEach(cards) { card in
               CardView(card: card)
                    .position(cardPosition)
                    .onReceive(self.timer) { _ in
                        moveDown()
                    }
            }
        }
        
    }
    
    func moveDown() {
        if cardPosition.y < UIScreen.main.bounds.height - 120 {
            withAnimation(.default){
                cardPosition.y += movingDownSpeed.y
            }
        }
    }
    
}
1 Answers

You can try:

struct TestView: View {
    var cards: [Card] = [
        Card(color: Color.red, name: "First Card"),
        Card(color: Color.green, name: "Second Card"),
        Card(color: Color.orange, name: "Third Card"),
    ]
    
    @State var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @State var movingDownSpeed = CGPoint(x: 0, y:120)
    @State var cardPosition = CGPoint(x: UIScreen.main.bounds.size.width * 0.5, y:75)
    
    @State var cardPositions = [CGPoint]()
    @State var count = 0

 
    var body: some View {
        ZStack {
            ForEach(self.cardPositions.indices, id: \.self) { index in
               CardView(card: cards[index])
                    .position(cardPositions[index])
            }
        }
        .onReceive(self.timer) { _ in
            moveDown()
        }
        .onAppear() {
            cardPositions = Array(repeating: CGPoint(x: UIScreen.main.bounds.size.width * 0.5, y:75), count:  cards.count)
            
        }
        
    }
    
    func moveDown() {
        if count < cardPositions.count {
            if cardPositions[count].y < UIScreen.main.bounds.height - 120 {
                withAnimation(.default){
                    cardPositions[count].y += movingDownSpeed.y
                }
            }
            count += 1
            if count == cardPositions.count {
                count = 0
            }
        }
    }
}
Related