Search code examples
animationswiftuigesture

How do I capture a tap on a custom card view in a ZStack?


I'm trying pretty hard to recreate something like the Wallet view, but cannot really figure it out. I've hard-coded offsets into my ZStack, but of course I want to avoid that too. It seems like the offsets are keeping the taps from registering at all. Here is my code so far, tapping a card should change its title's colour between white and yellow.

Eventually, of course, I want to add animations to the stack of cards and be able to "pull" one from it place to make it the primary view. But I have to figure out how to catch the taps first...

I used this video as a base, but it doesn't really go as far as I'd like:

https://nsscreencast.com/episodes/399-swiftui-transforms-and-animations

import SwiftUI

struct BetterCards: View {
    var body: some View {

        ScrollView (.vertical) {
                ZStack {
                    CardView(title: "adidas", color: .blue, offset: 0.0)
                    CardView(title: "Le Crueset", color: .pink, offset: 90.0)
                    CardView(title: " Card", color: .black, offset: 180.0)
                    CardView(title: "Sandusk", color: .orange, offset: 270.0)
            }
        }
    }
}


struct CardView: View {

    @State var isYellow: Bool = false

    let title: String
    let color: Color
    let offset: CGFloat

    var body: some View {


        Button(action: {
            self.isYellow.toggle()
        }) {
            ZStack {
                 Rectangle()
                    .fill(color)
                    .cornerRadius(10)
                    .frame(width: 320, height: 210)
                VStack {
                    Text(title)
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(isYellow ? .yellow : .white)
                        .padding()
                    Spacer()
                }

            }.shadow(radius: 2.0)
            .offset(x: 0, y: offset)

            }.onTapGesture {
            print("Tapped: \(self.title)")
        }
    }
}

struct BetterCards_Previews: PreviewProvider {
    static var previews: some View {
        BetterCards()
    }
}

Solution

  • Thank you both, Natalia and Mac3n

    It got me thinking and I've ended up using a VStack

    Happy to hear any comments. I'm not sure it's the best approach.

    I've done away with the offsets and used two frames in the card view - one for the rectangle and one for the ZStack. I'm still trying to figure out the math for the .frame on the VStack however. It seems to be a multiple of the number of cards + the real height of a card, plus some variable I can't pin down.

    import SwiftUI
    
    struct BetterCards: View {
    
        let multiplier:CGFloat = 6
    
        var body: some View {
    
            ScrollView {
                Text("HEADER").background(Color(.blue))
                VStack {
                    CardView(title: " Card", color: .black)
                    CardView(title: "adidas", color: .blue)
                    CardView(title: "Puma", color: .green)
                    CardView(title: "Le Crueset", color: .yellow)
    //                CardView(title: "adidas", color: .blue)
    //                CardView(title: "OnlyLyon", color: .green)
    //                CardView(title: "Le Crueset", color: .yellow)
                    CardView(title: "adidas", color: .blue)
                    CardView(title: "Sandusk", color: .orange)
    //                CardView(title: "Le Crueset", color: .pink)
                    }
                .frame(width: 400, height: multiplier * 60 + (430 - ( 52 )))
                .padding()
                .background(Color.gray)
                Text("FOOTER").background(Color(.blue))
            }
            .background(Color.yellow)
        }
    }
    
    struct CardView: View {
    
        @State var isYellow: Bool = false
    
        let title: String
        let color: Color
    
        var body: some View {
            ZStack {
                 Rectangle()
                    .fill(color)
                    .cornerRadius(10)
                    .frame(width: 370, height: 430)
                VStack {
                    Text(title)
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(isYellow ? .yellow : .white)
                        .padding()
                    Spacer()
                    }
            }
            .frame(width: 370, height: 60)
            .onTapGesture {
                    self.isYellow.toggle()
                    print("Tapped: \(self.title)")
            }
            .shadow(radius: 2.0)
        }
    }
    
    struct BetterCards_Previews: PreviewProvider {
        static var previews: some View {
            BetterCards()
        }
    }