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

NavigationSplitView详情视图仅首次点击生效问题求助

问题: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版本,有两种可靠的修复方式:

  1. 兼容全版本:使用@ObservedObject
    将UserDetailsView中的@StateObject替换为@ObservedObject:

    struct UserDetailsView: View {
        @ObservedObject var user: UserItem
        
        var body: some View {
            // 原有视图代码保持不变
        }
    }
    

    @ObservedObject用于观察外部传入的ObservableObject实例,当传入新实例时,视图会自动更新并绑定到新数据。

  2. 新版本推荐:使用@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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:12:07