Learning Time : 1h 15min

My Github : Flashzilla Source Code

My Note index link

day91.png

KeyWords

Challenge

  1. When adding a card, the textfields keep their current textfix that so that the textfields clear themselves after a card is added.
func addCard() {
    let trimmedPrompt = newPrompt.trimmingCharacters(in: .whitespaces)
    let trimmedAnswer = newAnswer.trimmingCharacters(in: .whitespaces)
    guard trimmedPrompt.isEmpty == false && trimmedAnswer.isEmpty == false else { return }
    
    let card = Card(prompt: trimmedPrompt, answer: trimmedAnswer)
    cards.insert(card, at: 0)
    
    saveData()
    
    newPrompt = ""
    newAnswer = ""
}
  1. If you drag a card to the right but not far enough to remove it, then release, you see it turn red as it slides back to the center. Why does this happen and how can you fix it? (Tip: think about the way we set offset back to 0 immediately, even though the card hasn’t animated yet. You might solve this with a ternary within a ternary, but a custom modifier will be cleaner.)
struct CardView: View {
    ...
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25, style: .continuous)
                .fill(
                    differentiateWithoutColor
                    ? .white
                    : .white.opacity(2 - Double(abs(offset.width / 50)))
                )
                .background(
                    differentiateWithoutColor
                    ? nil
                    : RoundedRectangle(cornerRadius: 25, style: .continuous)
                        .fill(of: offset)
                )
                .shadow(radius: 10)
            
            VStack {
                ...
            }
        }
        ...
    }
}

extension Shape {
    func fill(of offset: CGSize) -> some View {
        if offset.width == 0 {
            return self.fill(.white)
        } else if offset.width < 0 {
            return self.fill(.red)
        } else {
            return self.fill(.green)
        }
    }
}
  1. For a harder challenge: when the users gets an answer wrong, add that card goes back into the array so the user can try it again. Doing this successfully means rethinking the ForEach loop, because relying on simple integers isn’t enough – your cards need to be uniquely identifiable.
struct Card: Codable, Identifiable {
    var id = UUID()
    let prompt: String
    let answer: String
    
    static let example = Card(prompt: "Who played the 13th Doctor in Doctor Who?", answer: "Jodie Whittaker")
}

struct CardView: View {
    let card: Card
    var removal: ((Bool) -> Void)? = nil
    ...
    
    var body: some View {
        ZStack {
            ...
        }
        ...
        .gesture(
            DragGesture()
                .onChanged { gesture in
                    offset = gesture.translation
                    feedback.prepare()
                }
                .onEnded { _ in
                    if abs(offset.width) > 100 {
                        // remove the card
                        if offset.width > 0 {
                            feedback.notificationOccurred(.success)
                            removal?(true)
                        } else {
                            feedback.notificationOccurred(.error)
                            removal?(false)
                        }
                    } else {
                        offset = .zero
                    }
                }
        )
        ...
    }
}

struct ContentView: View {
    ...
    
    var body: some View {
        ZStack {
            ...
            
            VStack {
                ...
                
                ZStack {
                    ForEach(cards) { card in
                        CardView(card: card) { isCorrect in
                            withAnimation {
                                moveCard(card, isCorrect: isCorrect)
                            }
                        }
												.stacked(at: findIndex(of: card), in: cards.count)
                        .allowsHitTesting(findIndex(of: card) == cards.count - 1)
                        .accessibilityHidden(findIndex(of: card) < cards.count - 1)
                        
                    }
                }
                .allowsHitTesting(timeRemaining > 0)
                
                ...
            }
            
            ...
        }
        ...
    }

		func findIndex(of card: Card) -> Int {
		    cards.firstIndex(where: { $0.id == card.id }) ?? 0
		}
    
    func moveCard(_ card: Card, isCorrect: Bool) {
        guard let index = cards.firstIndex(where: { $0.id == card.id }) else { return }
        removeCard(at: index)
        
        if isCorrect == false {
            let reCard = Card(prompt: card.prompt, answer: card.answer)
            cards.insert(reCard, at: 0)
        }
    }
    
    func removeCard(at index: Int) {
        guard index >= 0 else { return }
        cards.remove(at: index)
        
        if cards.isEmpty {
            isActive = false
        }
    }
    
    func resetCards() {
        ...
    }
    
    func loadData() {
        ...
    }
}