如何实现带1秒间隔的数组倒计时打印?解决循环内延迟失效问题
解决Swift中for循环里延迟打印的倒计时问题
这个问题我之前也帮不少开发者梳理过,核心原因很明确:你的for循环会瞬间遍历完整个数组,所有DispatchQueue.main.asyncAfter(deadline: .now() + 1)的任务都会在循环结束后差不多同一时间(1秒后)被触发,所以所有数字会扎堆打印出来。
下面给你三种实用的解决方案,覆盖不同的场景需求:
方案1:用Timer实现传统倒计时(适合UI场景)
这是iOS/macOS开发中最常用的倒计时实现方式,逻辑清晰且稳定:
override func viewDidLoad() { super.viewDidLoad() let countdownArray = [9, 8, 7, 6, 5, 4, 3, 2, 1] var currentIndex = 0 // 每隔1秒触发一次定时器 let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in guard currentIndex < countdownArray.count else { timer.invalidate() // 遍历完成后销毁定时器,避免内存泄漏 return } print(countdownArray[currentIndex]) currentIndex += 1 } // 如果是在非RunLoop活跃的环境,需要手动添加定时器到RunLoop RunLoop.current.add(timer, forMode: .common) }
说明:定时器会按固定间隔触发,每次取数组的下一个元素打印,直到遍历完所有元素就自动停止,还能通过[weak self]避免循环引用。
方案2:用GCD延迟累加(轻量无定时器)
如果不想用Timer,也可以通过给每个元素的延迟时间加上索引偏移量来实现依次打印:
let countdownArray = [9, 8, 7, 6, 5, 4, 3, 2, 1] for (index, number) in countdownArray.enumerated() { // 第1个元素延迟1秒,第2个延迟2秒...以此类推 DispatchQueue.main.asyncAfter(deadline: .now() + Double(index + 1)) { print(number) } }
说明:通过enumerated()拿到每个元素的索引,把延迟时间设置为index+1秒,这样每个打印任务的触发时间就会依次错开1秒,完美解决同步触发的问题。
方案3:用Swift Async/Await(现代简洁写法,iOS15+)
如果你的项目支持iOS 15、macOS 12及以上版本,用异步等待的方式会更直观易读:
func startCountdown() async { let countdownArray = [9, 8, 7, 6, 5, 4, 3, 2, 1] for number in countdownArray { print(number) // 等待1秒(单位是纳秒,1秒=1e9纳秒) try? await Task.sleep(nanoseconds: 1_000_000_000) } } // 在viewDidLoad中调用(需要用Task包裹异步函数) override func viewDidLoad() { super.viewDidLoad() Task { await startCountdown() } }
说明:Task.sleep会让当前异步任务暂停指定时间,每次打印完元素后等待1秒,逻辑和我们手动倒计时的思路完全一致,代码非常简洁。
内容的提问来源于stack exchange,提问作者Kowboj
相关产品推荐
相关产品推荐

