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

SwiftUI中ViewModel间数据传递及视图更新问题求解

解决方案

核心问题分析

你当前的问题根源在于:@Binding 无法触发 ObservableObject(即 UserDetailsViewModel)的视图更新通知。SwiftUI 仅会监听 ObservableObject 中标记为 @Published 的属性变化,或者该对象本身替换时的事件,而 @Binding 属性的变更不会主动触发这个通知机制。

下面提供两种最优实现方案,均符合「视图仅与 ViewModel 交互」的整洁代码原则:


方案一:让 User 本身成为 ObservableObject(推荐)

将 User 定义为可观察对象,让其自身属性的变更直接驱动视图更新。两个 ViewModel 共享同一个 User 实例,天然实现数据同步。

修改后的代码示例

  1. 定义可观察的 User 模型
class User: ObservableObject {
    @Published var name: String
    @Published var details: String
    
    init(name: String, details: String) {
        self.name = name
        self.details = details
    }
}
  1. UserViewModel 实现
class UserViewModel: ObservableObject {
    @Published var user: User?
    var userDetailsViewModel: UserDetailsViewModel?

    func fetchUser() {
        APIClient.shared.loadUser { user in
            DispatchQueue.main.async {
                self.user = user
                self.userDetailsViewModel = UserDetailsViewModel(user: user)
            }
        }
    }
}
  1. UserDetailsViewModel 实现
class UserDetailsViewModel: ObservableObject {
    let user: User // 直接持有可观察的 User 实例

    init(user: User) {
        self.user = user
    }

    func updateUserName(_ name: String) {
        APIClient.shared.updateUserName(name) { updatedUser in
            DispatchQueue.main.async {
                // 直接更新 User 的属性,自动触发所有依赖视图的更新
                self.user.name = updatedUser.name
                self.user.details = updatedUser.details
                // 若接口返回完整 User 实例,也可直接替换:self.user = updatedUser
            }
        }
    }
}
  1. 视图部分无需大幅修改
struct UserView: View {
    @ObservedObject var viewModel = UserViewModel()

    var body: some View {
        VStack {
            Text(viewModel.user?.name ?? "")
            
            if let userDetailsViewModel = viewModel.userDetailsViewModel {
                NavigationLink {
                    UserDetailsView(viewModel: userDetailsViewModel)
                } label: {
                    Text("See user Details")
                }
            }
        }
        .onAppear {
            viewModel.fetchUser()
        }
    }
}

struct UserDetailsView: View {
    @ObservedObject var viewModel: UserDetailsViewModel

    var body: some View {
        VStack { 
            Text(viewModel.user.name)
            Text(viewModel.user.details)
        }
    }
}

方案优势

  • 数据模型自身具备可观察性,属性变更直接驱动视图更新,逻辑清晰
  • 两个 ViewModel 共享同一 User 实例,天然实现双向同步,无需额外绑定逻辑
  • 符合 MVVM 中「模型驱动视图」的核心思想,代码简洁易维护

方案二:让 UserDetailsViewModel 依赖 UserViewModel(兼容不可修改的 User 结构体)

如果 User 是第三方库提供的结构体、无法修改为类,可让 UserDetailsViewModel 持有 UserViewModel 的引用,通过观察父 ViewModel 的 @Published 属性实现同步。

修改后的代码示例

  1. User 保持原有结构体定义
struct User {
    var name: String
    var details: String
}
  1. UserViewModel 实现
class UserViewModel: ObservableObject {
    @Published var user: User?
    var userDetailsViewModel: UserDetailsViewModel?

    func fetchUser() {
        APIClient.shared.loadUser { user in
            DispatchQueue.main.async {
                self.user = user
                self.userDetailsViewModel = UserDetailsViewModel(parentViewModel: self)
            }
        }
    }
}
  1. UserDetailsViewModel 实现
class UserDetailsViewModel: ObservableObject {
    @ObservedObject private var parentViewModel: UserViewModel
    // 直接从父 ViewModel 获取 User 数据
    var user: User { parentViewModel.user! } // 实际开发建议添加安全解包逻辑

    init(parentViewModel: UserViewModel) {
        self.parentViewModel = parentViewModel
    }

    func updateUserName(_ name: String) {
        APIClient.shared.updateUserName(name) { updatedUser in
            DispatchQueue.main.async {
                // 更新父 ViewModel 的 User,自动触发两个视图的同步更新
                self.parentViewModel.user = updatedUser
            }
        }
    }
}

方案优势

  • 无需修改原有 User 结构体,兼容已有代码
  • 基于单一数据源原则,所有数据变更都通过父 ViewModel 统一处理,避免数据不一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 23:01:36