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

SwiftUI Sheet修改分数后未更新父视图玩家区域的问题求解

问题根因

你当前使用的Game、Player、Round均为值类型(struct),在sheet的ForEach中传递player给View1时实际是做了值拷贝,View1.ViewModel内修改的是副本属性,和ContentView中game.players存储的原始实例无关联,因此不会触发父视图更新。

修复方案

1. 调整数据模型为引用类型

将需要跨视图同步修改的模型改为class类型,需观测的属性添加@Published标记:

// Round.swift
import SwiftUI

class Round: Identifiable, ObservableObject {
    var id: UUID = UUID()
    @Published var earnedPoints: Int = 0
    var completedPhase: Bool = false
    var selectedPhase: Int = 0
}
// Player.swift
import SwiftUI

class Player: Identifiable, ObservableObject {
    init(name: String) {
        self.name = name
    }
    var id: UUID = UUID()
    var name: String
    @Published var rounds: [Round] = [Round()]
    
    var score: Int {
        rounds.map(\.earnedPoints).reduce(0, +)
    }
}
// Game.swift
import SwiftUI

class Game: Identifiable, ObservableObject {
    init(name: String) {
        self.name = name
    }
    var id: UUID = UUID()
    var name: String
    @Published var players: [Player] = []
    var isMastersEdition: Bool = false
}

2. 调整ContentView的属性持有方式

将game改为@StateObject持有,遍历玩家时添加@ObservedObject监听单个玩家的属性变化:

import SwiftUI

struct ContentView: View {
    @StateObject var game: Game = Game(name: "Game #1")
    @State var isPresented: Bool = true
    
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("PLAYERS")) {
                    ForEach(game.players) { player in
                        @ObservedObject var player = player
                        HStack {
                            Text(player.name)
                            Spacer()
                            Text("\(player.score)")
                        }
                    }
                }
            }
            .navigationTitle(game.name)
        }
        .sheet(isPresented: $isPresented) {
            TabView {
                ForEach(game.players) { player in
                    View1(player: player)
                }
            }
            .tabViewStyle(PageTabViewStyle())
        }
        .onAppear {
            for p in 1...3 {
                let player = Player(name: "Player #\(p)")
                game.players.append(player)
            }
        }
    }
}

3. 调整View1及其ViewModel的实现

ViewModel中监听round的属性变化,手动触发更新通知,需导入Combine框架:

import SwiftUI
import Combine

struct View1: View {
    @ObservedObject var vm: ViewModel
    
    init(player: Player) {
        vm = ViewModel(player: player)
    }
    
    var body: some View {
        VStack {
            Spacer()
            Text(vm.name)
            Divider()
            Group {
                Stepper("Earned Points: \(vm.earnedPoints)", value: $vm.earnedPoints, in: 0...50)
            }
            .padding(.horizontal)
            Spacer()
        }
    }
}

extension View1 {
    class ViewModel: ObservableObject {
        private let player: Player
        private let round: Round
        private var cancellables = Set<AnyCancellable>()
        
        init(player: Player) {
            self.player = player
            self.round = player.rounds[0]
            
            // 监听round的属性变化,触发ViewModel的更新通知
            round.objectWillChange
                .sink { [weak self] _ in
                    self?.objectWillChange.send()
                }
                .store(in: &cancellables)
        }
        
        var name: String { player.name }
        
        var earnedPoints: Int {
            get { round.earnedPoints }
            set { round.earnedPoints = newValue }
        }
    }
}

修改完成后,Stepper修改的分数会实时同步到父视图的玩家分数区域,无需等待sheet关闭才更新。


内容的提问来源于stack exchange,提问作者eleethesontai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 05:36:02