Delay in For Loop Swift

女生的网名这么多〃 提交于 2021-01-29 06:51:25

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!