UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Day 91: Flashzilla challenge #2

Forums > 100 Days of SwiftUI

I'm having real trouble with the second challenge in the Flashzilla project. I'm trying to work out how to tell from the removeCard function whether a card was answered correctly or incorrectly, and I started out trying to modify the closure so that I could call self.removal?(correctAnswer) after the DragGesture in CardView.swift. However, I've tried a number of ways to get this to work and I'm really struggling to do so – can anyone give me any hints as to how they've done this?

4      

I have a same problem here and I've been stuck on this for a while now :( : https://www.hackingwithswift.com/forums/100-days-of-swiftui/day-91-flashzilla-challenge-2-3/7793

Hope you'll get some response cause I'd like to move forward and it's really frustrating :(

4      

I did eventually work this out! I went back to the Unwrap app and revised my closures, and came up with the below.

In ContentView.swift I inserted this code in place of the previous code:

let removal = { (correct: Bool) in
    print(correct)
    if self.removeWrongAnswers || correct {
        withAnimation {
            self.removeCard(at: index)
        }
    } else {
        // the DispatchQueue bit fixes the bug (source: HwS forums)
        let wrongCard = self.cards.remove(at: index)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.cards.insert(wrongCard, at: 0)
        }
    }
}

CardView(card: self.cards[index], removal: removal)
    .stacked(at: index, in: self.cards.count)
    .allowsHitTesting(index == self.cards.count - 1)
    .accessibility(hidden: index < self.cards.count - 1)

and then in CardView.swift I inserted this code in place of the existing .onEnded modifier:

.onEnded { _ in
    if abs(self.offset.width) > 100 {
        var correct: Bool

        if self.offset.width > 0 {
            self.feedback.notificationOccurred(.success)
            correct = true
        } else {
            self.feedback.notificationOccurred(.error)
            correct = false
        }

        self.removal?(correct)
    } else {
        self.offset = .zero
    }
}

7      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

@johncoxon Thansk!! It's working for me now too :)

5      

OMG, I've just spent so long trying to figure this out.

I've then tried to implement your code but couldn't get that to work either. Then I noticed the comment about about the bug and the dispatch queue code.

Turns out my first attempt yesterday was working except for the bug... I've just added the dispatch queue code around my card insertion and boom, all working fine.

So frustrating, but also kind of a relief that I was so close to resolving this except for the bug. Incidentally, do you have a link to a discussion around the bug?

edit:

Don't worry, I've found it: https://www.hackingwithswift.com/forums/100-days-of-swiftui/day-91-flashzilla-challenges-can-t-seem-to-readd-a-card-successfully/2037/6401

4      

Hello everyone! I'm also stucked on this challenge too. But I found better solution with less code and without DispatchQueue.main.

So Paul said that we should rethink our Card model and add an UUID(). When you create your List() it's identify your objects by UUID of the Card. And it's cause our problems. SwifUI manage object with the same UUID trying to remove from Array and append to it back almost at the same time .

So when you are trying to move Card to the end just set a new UUID() to the card. And everything will works fine without using delay on DispathchQueue.

3      

I'm also and still having trouble, even after integrating Paul's solution. He hasn't shown this specific case in the video, so I don't know whether it would work for him or not.

First of all, I'm not uising remove and insert, but

if reinsert {
    cards.move(fromOffsets: IndexSet(integer: index), toOffset: 0)
} else {
    cards.remove(at: index)
}

and my list is using Array(cards.enumerated()) with item.element and item.offset. (Card has UUID and is Identifiable and Hashable).

However it still causes problems in certain circumstances. If I swipe everything to left only, it seems to work.

As soon as I swipe one element to the right and I reach the former end of the list, the next item is not clickable. Easy to reproduce: Just swipe everything to right, then the last element to left. It reappears, but is not draggable.

Weird combinations also exist with some left, some right and from some elment on, it's not draggable anymore.

Anyone else facing this issue?

3      

Nevermind… comparison line by line revealed one wrong value to the reinsert parameter (both got a true, but when swiping to the right, the offset asn't reset to .zero. That caused the unexpected behavior.

3      

@Hakim  

Made Solution without dispatch!)

https://github.com/hakim-cyber/FlashZilla

3      

Hi everyone,

so I found a really simple solution to this problem we all have. I see there are solutions with DispatchQueue and that's great but there is delay that you need to have for it to work.

I have found a better solution for that problem and you do not need to use DispatchQueue. It is pretty much simple. Here are the steps:

  1. You need to make Card struct conform Identifiable

    struct Card: Codable, Identifiable {
    var id = UUID()
    let prompt: String
    let answer: String
    
    static let example = Card(prompt: "What is capital city of Croatia?", answer: "Zagreb")
    }
  2. You need to create a function that will get you a index from your card. Here is my solution of that function but you can make it different

    func getIndex(of card: Card) -> Int {
        for i in 0...cards.count {
            if cards[i].id == card.id {
                return i
            }
        }
        return -1
    }
  3. When you have that function you can change your ForEach and simplify it

    ForEach(cards) { card in
    //code
    }
  4. And to finish all up, wherever you need index you just call your getIndex(card: card) function. And that's it! Here is finished ForEach:

    ForEach(cards) { card in
          CardView(card: card) { isCorrect in
              if !isCorrect {
                  withAnimation {
                      let newCard = Card(id: UUID(), prompt: card.prompt, answer: card.answer)
                      cards.remove(at: getIndex(of: card))
                      cards.insert(newCard, at: 0)
                  }
              } else {
                  withAnimation {
                      removeCard(at: getIndex(of: card))
                  }
              }
    
          }
          .stacked(at: getIndex(of: card), in: cards.count)
          .allowsHitTesting(getIndex(of: card) == cards.count - 1)
          .accessibilityHidden(getIndex(of: card) < cards.count - 1)
      }

Keep it going!

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.