krz/daybyday
clone: git clone https://gitbay.org/krz/daybyday.git
main: AnimalCard.swift · raw
1//
2// AnimalCard.swift
3// DayByDay
4//
5
6import SwiftUI
7
8/// A single large, colorful card representing one animal.
9/// Tapping the card plays a bounce animation and speaks the animal name aloud.
10struct AnimalCard: View {
11 let animal: LearnAnimal
12 @State private var isTapped = false
13
14 var body: some View {
15 ZStack {
16 RoundedRectangle(cornerRadius: 32, style: .continuous)
17 .fill(animal.color.gradient)
18 .shadow(color: animal.color.opacity(0.4), radius: 8, y: 4)
19
20 VStack(spacing: 16) {
21 Image(systemName: animal.symbol)
22 .font(.system(size: 64))
23 .foregroundStyle(.white)
24 .symbolRenderingMode(.hierarchical)
25
26 Text(animal.name)
27 .font(.system(size: 32, weight: .bold, design: .rounded))
28 .foregroundStyle(.white)
29 }
30 }
31 .scaleEffect(isTapped ? 1.12 : 1.0)
32 .contentShape(RoundedRectangle(cornerRadius: 32, style: .continuous))
33 .rotation3DEffect(.degrees(isTapped ? 6 : 0), axis: (x: 1, y: 0, z: 0))
34 .animation(.spring(response: 0.35, dampingFraction: 0.5), value: isTapped)
35 .onTapGesture {
36 isTapped = true
37 SpeechSynthesizer.shared.speak(animal.name)
38 DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
39 isTapped = false
40 }
41 }
42 .accessibilityLabel(animal.name)
43 .accessibilityAddTraits(.isButton)
44 }
45}
46
47// MARK: - Animal model
48
49enum LearnAnimal: Int, CaseIterable, Identifiable {
50 case cat, dog, bird, fish, rabbit, turtle, ladybug, ant, lizard
51
52 var id: Int { rawValue }
53
54 var name: String {
55 switch self {
56 case .cat: "Cat"
57 case .dog: "Dog"
58 case .bird: "Bird"
59 case .fish: "Fish"
60 case .rabbit: "Rabbit"
61 case .turtle: "Turtle"
62 case .ladybug: "Ladybug"
63 case .ant: "Ant"
64 case .lizard: "Lizard"
65 }
66 }
67
68 var symbol: String {
69 switch self {
70 case .cat: "cat.fill"
71 case .dog: "dog.fill"
72 case .bird: "bird.fill"
73 case .fish: "fish.fill"
74 case .rabbit: "hare.fill"
75 case .turtle: "tortoise.fill"
76 case .ladybug: "ladybug.fill"
77 case .ant: "ant.fill"
78 case .lizard: "lizard.fill"
79 }
80 }
81
82 var color: Color {
83 switch self {
84 case .cat: Color(red: 0.9, green: 0.55, blue: 0.2) // orange
85 case .dog: Color(red: 0.6, green: 0.45, blue: 0.3) // brown
86 case .bird: Color(red: 0.3, green: 0.65, blue: 0.9) // sky blue
87 case .fish: Color(red: 0.2, green: 0.75, blue: 0.8) // teal
88 case .rabbit: Color(red: 0.75, green: 0.6, blue: 0.8) // lavender
89 case .turtle: Color(red: 0.35, green: 0.7, blue: 0.4) // green
90 case .ladybug: Color(red: 0.9, green: 0.25, blue: 0.25) // red
91 case .ant: Color(red: 0.3, green: 0.3, blue: 0.3) // dark gray
92 case .lizard: Color(red: 0.45, green: 0.75, blue: 0.35) // lime
93 }
94 }
95}
96
97#Preview {
98 HStack(spacing: 24) {
99 AnimalCard(animal: .cat)
100 .frame(width: 200, height: 260)
101 }
102 .padding()
103}