You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 07:48:04