SwiftUI中onChange更新EnvironmentObject触发异常回调问题
我在BranchListViewModel(ObservableObject子类)中使用@Published修饰的inProgress布尔变量跟踪API请求状态:调用setActive()方法时将其设为true,请求结束后设为false。原本在BranchListView中通过.onChange(of: branchViewModel.inProgress)监控该变量,当值为false时调用reloadData,运行正常。
但当我在该onChange回调中更新EnvironmentObject类型的routerViewModel的showLoadingIndicator为当前值时,出现异常:调用branchViewModel.setActive()后,inProgress设为true,onChange先收到true,随即又收到false;待API请求结束将inProgress设为false时,onChange不再触发。而inProgress的didSet显示其值变化正确(true→false)。
异常流程:
branchViewModel.setActive() inProgress = true inProgress didSet -> true .onChange(of:) -> true .onChange(of:) -> false <<<<--- 异常点 API call started API call processing API call finished inProgress = false inProgress didSet -> false
解决方案
将加载状态更新逻辑移到ViewModel内部
避免在视图的onChange回调中修改EnvironmentObject,防止触发不必要的视图刷新导致状态混乱。直接在BranchListViewModel中同步更新routerViewModel的加载状态:class BranchListViewModel: ObservableObject { @Published var inProgress = false private let routerViewModel: RouterViewModel init(routerViewModel: RouterViewModel) { self.routerViewModel = routerViewModel } func setActive() { inProgress = true routerViewModel.showLoadingIndicator = true // 启动异步API请求 Task { await performAPICall() inProgress = false routerViewModel.showLoadingIndicator = false } } private func performAPICall() async { // 你的API请求逻辑 } }用
.task替代.onChange处理状态响应.task会绑定到指定状态的变化,不会因视图刷新重复触发不必要的回调,更适合处理异步相关的状态响应:BranchListView() .task(id: branchViewModel.inProgress) { routerViewModel.showLoadingIndicator = branchViewModel.inProgress if !branchViewModel.inProgress { reloadData() } }排查
showLoadingIndicator的其他修改源
给routerViewModel的showLoadingIndicator添加didSet日志,确认是否有其他视图或逻辑在修改该值,导致触发额外的视图刷新干扰inProgress的回调:class RouterViewModel: ObservableObject { @Published var showLoadingIndicator = false { didSet { print("showLoadingIndicator changed: \(oldValue) → \(showLoadingIndicator)") } } }
内容的提问来源于stack exchange,提问作者filip.karas

