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

SwiftUI中onAppear方法无法更新State,请求数据后视图不刷新如何解决?

问题解决方法

核心原因

  • 网络请求回调默认运行在后台子线程,SwiftUI要求所有触发UI刷新的@State变量更新必须在主线程执行,你当前直接在后台回调中修改todos,不会触发UI重绘,因此页面停留在初始默认数据。
  • onAppear中添加的DispatchQueue.main.async仅将getTodos()的调用切到了主线程,fetchTodos的异步回调依然运行在后台线程,没有解决根本问题。

修复方案

快速修复(适配现有代码结构)

  1. 修改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)
        }
    }
}
  1. 清理onAppear中的冗余主线程调度代码:
.onAppear(){
    getTodos()
}

可选优化(适配SwiftUI MVVM规范)

如果后续ViewModel需要承载更多业务逻辑,推荐将其改造为遵守ObservableObject的类类型,状态管理更清晰:

  1. 修改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)
    }
}
  1. 修改ContentView的状态声明:
// 用@StateObject修饰ObservableObject类型的ViewModel
@StateObject var todos = TodoListViewModel()
  1. 调整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
        }
    }
}
  1. App入口初始化时可以不用传入默认值,直接初始化ContentView()即可。

内容的提问来源于stack exchange,提问作者Emre D.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 19:45:06