SwiftUI如何向子视图传递可更新的只读数据
解决SwiftUI子视图只读展示ObservableObject数据的问题
你现在的代码用@Binding传递单个属性,确实存在子视图意外修改数据的风险。更稳妥的做法是直接传递整个User实例给ScoreView,利用@ObservedObject监听数据变化,同时保证子视图仅做只读展示。
具体改法:
调整ScoreView,接收完整的User对象
用@ObservedObject修饰User实例,这样视图能自动响应@Published属性的变化,同时子视图只能通过对象读取属性,不会有直接修改父视图数据的绑定能力。修改ContentView的调用方式
直接把player和cpu实例传入ScoreView,不用加$创建绑定。可选优化:让User的name不可修改
如果name不需要后续变更,可以把它设为let,彻底避免被意外修改。
修改后的完整代码:
User类(可选优化name为let)
class User: ObservableObject { let name: String // 设为let,防止被修改 @Published var score: Int init(name: String, score: Int) { self.name = name self.score = score } }
ContentView
struct ContentView: View { @StateObject var player = User(name: "Player", score: 0) @StateObject var cpu = User(name: "CPU", score: 0) var body: some View { ZStack { Image("background-plain").resizable().ignoresSafeArea() VStack { Spacer() Image("logo") Spacer() HStack { Spacer() ScoreView(user: player) // 直接传实例 Spacer() ScoreView(user: cpu) Spacer() } .fontWeight(.semibold) .foregroundColor(.white) Spacer() } } } }
ScoreView
struct ScoreView: View { @ObservedObject var user: User // 用@ObservedObject监听变化 var body: some View { VStack { Text(user.name) .font(.headline) .padding(.bottom, 10) Text(String(user.score)) .font(.largeTitle) } } }
为什么这样更好?
@ObservedObject能让ScoreView自动响应User中@Published属性(比如score)的变化,完全满足你要同步更新的需求。- 子视图只能通过
user.name和user.score读取数据,没有绑定的修改权限——就算想改,name是let的话直接编译报错,score要修改得写user.score = ...,但从视图职责来说,我们不会在纯展示的ScoreView里写这种代码,比用@Binding安全多了。 - 传递完整对象的方式更符合面向对象设计,后续如果要给User加其他属性,也不用改ScoreView的参数,扩展性更强。
内容的提问来源于stack exchange,提问作者Charklewis
相关产品推荐
相关产品推荐

