SwiftUI中进入GameView时如何每次重新洗牌数组?
当前代码的问题
你的NavigationLink的destination闭包仅在**首次创建GameView**时执行一次。当返回MenuView再重新进入时,由于NavigationStack的视图缓存机制,闭包内的洗牌操作不会重复执行,导致sequence保持旧的顺序,所以卡牌序列不变。
MenuView.onAppear()洗牌的合理性
这个方案能实现需求,但逻辑不够内聚——洗牌是GameView的业务逻辑,却依赖MenuView的生命周期触发。如果后续新增其他进入GameView的入口(比如从设置页跳转),这个逻辑会失效,维护成本较高,不是最优解。
两种更优的实现方案
方案1:在GameView的.onAppear()中洗牌(复用父视图状态)
修改GameView,利用onAppear生命周期钩子,每次进入时重新生成洗牌序列:
struct GameView: View { @Binding var sequence: [Int] @State var cardNumber = 0 var body: some View { ZStack { BackgroundComponent() VStack { CardComponent(cardDifficulty: cardArray[sequence[cardNumber]].difficulty) } } .onAppear { // 每次进入GameView时重新洗牌 sequence = (0..<cardArray.count).shuffled() // 重置当前显示的卡牌索引,确保每次从第一张开始 cardNumber = 0 } } }
同时简化MenuView中的NavigationLink,不需要在闭包里手动修改sequence:
NavigationLink(destination: GameView(sequence: $sequence), label: { PrimaryButton(text: "PLAY") })
优点:保持sequence在父视图的状态管理,同时将洗牌逻辑与GameView的进入行为绑定,符合需求逻辑。
方案2:GameView独立管理sequence(推荐,逻辑内聚)
如果GameView不需要将sequence暴露给MenuView,直接把序列状态移到GameView内部,实现完全独立的逻辑:
struct GameView: View { @State private var sequence: [Int] = [] @State var cardNumber = 0 var body: some View { ZStack { BackgroundComponent() VStack { // 确保序列初始化后再渲染卡牌 if !sequence.isEmpty { CardComponent(cardDifficulty: cardArray[sequence[cardNumber]].difficulty) } } } .onAppear { // 每次进入时生成新的洗牌序列 sequence = (0..<cardArray.count).shuffled() cardNumber = 0 } } }
MenuView的NavigationLink也无需传递sequence:
NavigationLink(destination: GameView(), label: { PrimaryButton(text: "PLAY") })
优点:GameView的逻辑完全独立,符合单一职责原则,后续新增功能或修改逻辑时更易维护。
关于“避免连续重复显示卡牌”
当前的shuffled()方法已经能生成无重复元素的随机序列,只要按sequence的顺序依次显示卡牌,就不会出现连续重复的情况。如果需要额外确保序列中相邻元素不重复,可以自定义洗牌逻辑(比如生成后检查相邻元素,若重复则调整),但当前方案已满足你“每次进入时序列不重复”的核心需求。
内容的提问来源于stack exchange,提问作者TheDoctor

