SwiftUI中onAppear方法无法更新State,请求数据后视图不刷新如何解决?
问题解决方法
核心原因
- 网络请求回调默认运行在后台子线程,SwiftUI要求所有触发UI刷新的
@State变量更新必须在主线程执行,你当前直接在后台回调中修改todos,不会触发UI重绘,因此页面停留在初始默认数据。 onAppear中添加的DispatchQueue.main.async仅将getTodos()的调用切到了主线程,fetchTodos的异步回调依然运行在后台线程,没有解决根本问题。
修复方案
快速修复(适配现有代码结构)
- 修改
getTodos方法,将状态赋值操作切换到主线程执行:
func getTodos(){ let url = URL(string:"https://xxx.xxx.xxx/xxx/xx/xxxx")! TodoWebService().fetchTodos(url: url) { (todoList) in guard let todoList = todoList else { return } // 切换到主线程更新State,触发UI刷新 DispatchQueue.main.async { self.todos = TodoListViewModel(todolist: todoList) } } }
- 清理
onAppear中的冗余主线程调度代码:
.onAppear(){ getTodos() }
可选优化(适配SwiftUI MVVM规范)
如果后续ViewModel需要承载更多业务逻辑,推荐将其改造为遵守ObservableObject的类类型,状态管理更清晰:
- 修改
TodoListViewModel实现:
class TodoListViewModel: ObservableObject { // @Published修饰的属性变更时会自动通知视图刷新 @Published var todolist : [Todo] = [] func numberOfRows() -> Int { return self.todolist.count } func todoAtIndex(_ index:Int) -> TodoViewModel { let todo = self.todolist[index] return TodoViewModel(todo: todo) } }
- 修改
ContentView的状态声明:
// 用@StateObject修饰ObservableObject类型的ViewModel @StateObject var todos = TodoListViewModel()
- 调整
getTodos的赋值逻辑:
func getTodos(){ let url = URL(string:"https://xxx.xxx.xxx/xxx/xx/xxxx")! TodoWebService().fetchTodos(url: url) { (todoList) in guard let todoList = todoList else { return } DispatchQueue.main.async { // 直接给@Published属性赋值即可自动触发刷新 self.todos.todolist = todoList } } }
- App入口初始化时可以不用传入默认值,直接初始化
ContentView()即可。
内容的提问来源于stack exchange,提问作者Emre D.
相关产品推荐
相关产品推荐

