SwiftUI 函数调用内部如何给视图添加延迟 实现二十一点逐张发牌效果
问题核心原因
你现有代码的问题在于:for 循环会瞬间执行完成,所有 Timer 会在同一时间点被调度,触发间隔都设为1秒,所以4次给hand数组追加元素的操作会在点击按钮1秒后几乎同时执行,触发@State变量一次性刷新,最终表现为所有卡牌同时展示。同时你当前的分数计算逻辑直接写在循环体中,会在点击按钮瞬间就完成计算,和UI展示不同步。
解决方案
方案1:叠加延迟的DispatchQueue实现
你可以给每一步发牌操作设置递增的延迟时间,同时把卡牌追加、分数计算逻辑都放到对应的异步回调中,保证逻辑和UI同步:
// 先把要发的4张牌预先取出来,避免异步操作时deck状态错乱 let card1 = deck.removeLast() // 玩家第一张 let card2 = deck.removeLast() // 庄家第一张暗牌 let card3 = deck.removeLast() // 玩家第二张 let card4 = deck.removeLast() // 庄家第二张明牌 // 第1秒:发玩家第一张 DispatchQueue.main.asyncAfter(deadline: .now() + 1) { player1.hand.append(card1) // 玩家分数计算 switch card1.rank { case 1: player1.cardScore += 1 player1.hasAce = true case 11,12,13: player1.cardScore += 10 default: player1.cardScore += card1.rank } } // 第2秒:发庄家第一张暗牌 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { dealer.hand.append(card2) } // 第3秒:发玩家第二张 DispatchQueue.main.asyncAfter(deadline: .now() + 3) { player1.hand.append(card3) // 玩家分数计算 switch card3.rank { case 1: player1.cardScore += 1 player1.hasAce = true case 11,12,13: player1.cardScore += 10 default: player1.cardScore += card3.rank } } // 第4秒:发庄家第二张明牌 DispatchQueue.main.asyncAfter(deadline: .now() + 4) { dealer.hand.append(card4) // 庄家第二张算分 switch card4.rank { case 1: dealer.cardScore += 1 dealer.hasAce = true case 11,12,13: dealer.cardScore += 10 default: dealer.cardScore += card4.rank } isDealt = true }
方案2:更简洁的async/await实现
如果你的项目支持iOS 15+,可以用Swift Concurrency的async/await写法,代码逻辑更直观,不需要手动计算延迟时间:
// 发牌函数 @MainActor func dealCards() async { // 发玩家第一张 let card1 = deck.removeLast() player1.hand.append(card1) // 玩家分数计算逻辑和方案1一致,此处省略 try? await Task.sleep(nanoseconds: 1_000_000_000) // 等待1秒 // 发庄家第一张暗牌 let card2 = deck.removeLast() dealer.hand.append(card2) try? await Task.sleep(nanoseconds: 1_000_000_000) // 发玩家第二张 let card3 = deck.removeLast() player1.hand.append(card3) // 玩家分数计算 try? await Task.sleep(nanoseconds: 1_000_000_000) // 发庄家第二张明牌 let card4 = deck.removeLast() dealer.hand.append(card4) // 庄家分数计算 isDealt = true } // 按钮点击处调用 Button("发牌") { Task { await dealCards() } }
注意事项
如果你的Player是引用类型(class),不要用@State修饰,应该改用@StateObject(苹果推荐的引用类型状态管理修饰符),否则类内部的属性变更可能无法触发SwiftUI的UI刷新。
内容的提问来源于stack exchange,提问作者Jcipa
相关产品推荐
相关产品推荐

