NavigationSplitView详情视图仅首次点击生效问题求助
问题描述
我编写了一款macOS示例应用,内置3个用户数据。在NavigationSplitView中为每个用户配置NavigationLink,预期点击任意用户的NavigationLink后,详情视图会进入编辑模式展示该用户的姓名与年龄。但实际仅点击第一个用户时功能正常,点击其他用户时详情视图无变化,始终显示第一个用户的数据。
我是SwiftUI新手,但拥有20余年其他领域编程经验。已通过谷歌(尤其Stack Overflow)查找解决方案多日,仍未解决问题,恳请各位帮忙指出错误。
相关代码
// Create users array class Users: ObservableObject { @Published var users: [UserItem] = [ UserItem(id: 0, nickName: "Nick1", familyName: "Family1", firstName: "First1", age: 42), UserItem(id: 1, nickName: "Nick2", familyName: "Family2", firstName: "First2", age: 39), UserItem(id: 2, nickName: "Nick3", familyName: "Family3", firstName: "First3", age: 35) ] } // Define user properties class UserItem: Identifiable, ObservableObject { @Published var id:Int @Published var nickName:String @Published var familyName:String @Published var firstName:String @Published var age:Int init(id:Int, nickName:String, familyName:String, firstName:String, age:Int) { self.id = id self.age = age self.familyName = familyName self.firstName = firstName self.nickName = nickName } func fullName() -> String { return "\(self.familyName) \(self.firstName) (\(self.nickName)) \(self.age)" } } // Show users and their detail struct Test2: View { @StateObject var usersData = Users() var body: some View { NavigationSplitView { List { ForEach(usersData.users) { user in NavigationLink { UserDetailsView(user: user) } label:{ Text(user.fullName()) } } Spacer() } } detail: { Text("Select a user to edit.") } .padding() .navigationTitle("Users") } } // Show user detail struct UserDetailsView: View { @StateObject var user: UserItem var body: some View { VStack { TextField("Family name", text: $user.familyName) TextField("First name", text: $user.firstName) TextField("Nick name", text: $user.nickName) Stepper("Age: \(user.age)", value: $user.age) Spacer() // Check user property (live update)) Text(user.fullName()) }.padding() } } #Preview { Test2() }
问题原因与修复
问题核心在于UserDetailsView中错误使用了@StateObject修饰传入的user参数:
@StateObject var user: UserItem
@StateObject的作用是初始化并拥有一个ObservableObject实例,它会在视图首次初始化时创建对象并锁定其生命周期。当你通过NavigationLink传入新的UserItem实例时,@StateObject会忽略后续传入的对象,始终使用第一次初始化时的实例,这就是点击其他用户时详情视图无变化的原因。
修复方案
根据你的macOS版本,有两种可靠的修复方式:
兼容全版本:使用
@ObservedObject
将UserDetailsView中的@StateObject替换为@ObservedObject:struct UserDetailsView: View { @ObservedObject var user: UserItem var body: some View { // 原有视图代码保持不变 } }@ObservedObject用于观察外部传入的ObservableObject实例,当传入新实例时,视图会自动更新并绑定到新数据。新版本推荐:使用
@Bindable(macOS 14+ / iOS 17+)
如果你使用的是较新的系统版本,推荐使用@Bindable,它语义更清晰且性能更优:struct UserDetailsView: View { @Bindable var user: UserItem var body: some View { // 原有视图代码保持不变 } }@Bindable允许直接绑定ObservableObject的@Published属性,同时会响应实例的更换,是SwiftUI新版本中处理外部传入ObservableObject的首选方式。
额外说明
- 你的
UserItem类实现没有问题,作为ObservableObject并使用@Published修饰属性的逻辑是正确的。 Test2视图中使用@StateObject修饰usersData是正确的,因为它是在该视图内部初始化并拥有的数据源。
内容的提问来源于stack exchange,提问作者Louie88

