问题
I am creating a simple card game (Set) in SwiftUI. I have a button that will deal X new cards when tapped. Currently, it makes all cards show up at once. I was wondering how I could make them come out one at a time.
Deal works by appending a new card to a Deck array in the model. ContentView displays each card in the grid.
This is what I currently have after looking online. Displays first card then next all at once
func deal(_ numberOfCards: Int) {
withAnimation(Animation.easeInOut(duration: 1)) {
viewModel.deal()
}
for _ in 1..<numberOfCards {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
withAnimation(.easeInOut) {
viewModel.deal()
}
}
}
}
回答1:
Try this
func deal(_ numberOfCards: Int) {
withAnimation(Animation.easeInOut(duration: 1)) {
viewModel.deal()
}
for i in 1..<numberOfCards {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.7) {
withAnimation(.easeInOut) {
viewModel.deal()
}
}
}
}
回答2:
The problem is that you’re starting all of them 0.7 seconds from now. You want to multiply that interval by the for loop index. You can probably also simplify it a bit, e.g.:
func deal(_ numberOfCards: Int) {
for i in 0..<numberOfCards {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.7) {
withAnimation(.easeInOut) {
viewModel.deal()
}
}
}
}
This pattern isn’t ideal, though, because if you dismiss the view in question, it’s still going to be trying to flip cards on view that isn’t present anymore. Also, this pattern of issuing multiple asyncAfter is not great because it’s subject to timer coalescing (where latter calls may be coalesced together to save battery). This latter issue might not be an issue here, but as a general rule, we use Timer to prevent coalescing and to be able to cancel the timer when the view is dismissed.
func deal(_ numberOfCards: Int) {
var cardNumber = 0
let timer = Timer.scheduledTimer(withTimeInterval: 0.7, repeats: true) { timer in
withAnimation(.easeInOut) {
viewModel.deal()
}
cardNumber += 1
if cardNumber >= numberOfCards {
timer.invalidate()
}
}
timer.fire()
}
If this was in a class, I might use [weak self] in the timer closure with
guard let self = self else {
timer.invalidate()
return
}
来源:https://stackoverflow.com/questions/62628312/delay-in-for-loop-swift