如何管理 UI 状态?可行方案3选1,AI 别乱写
- 只有 View @State 持有状态,通过 UI 回调或者查询一个无状态的 actor 来更新
- 当且仅当状态本身与逻辑无关,仅负责显示。如文件名列表
- 只有 ObservableObject 持有状态,View 直接持有 ObservableObject 并访问其中的 @Published
- 当且仅当某个 UI 状态是轻量后端逻辑的主动触发者。如页面切换管理者需要在切换页面的同时暂停 ARSession,想要将两者绑定管理
- actor 持有真实状态,对 actor 注入一个 ObservableObject,ObservableObject 持有真实状态的拷贝,UI 引用该 ObservableObject 并访问其中的 @Published
- 当且仅当某个 UI 状态是重量后端逻辑的主动触发者。如录制器的已开始、结束中、取消中,想要和真实的录制逻辑绑定管理,此时多个 await 会有严重代价
- 或:某个 UI 状态是后端自发变化状态的映射。如网络管理器自发变化时需要 UI 显示已连接设备
- 需要测试的是,在其他 actor 执行 await MainActor 是否有 16.7ms 代价
对于 1 的例子
1struct UploadView: View {2 @State var list: []3 @State var text: String4 let finder: UploadFinder // 假设这是 actor5
6 init(finder) { self.finder = finder } // 注入7
8 body {9 Button { self.text = "clicked" } // button 回调10 }11 .onAppear {12 self.list = await self.finder.findAllFiles() // 无状态的查询13 }14}对于 2 的例子
1struct UploadView: View {2 @ObservedObject var page: PageCoodinator3
4 init(page) { self.page = page }5
6 body {7 switch self.page.currentPage { case ... SomeView() }8 Button { self.page.changePage() }9 }10 .onAppear {11 }12}13
14class PageCoodinator: @ObservableObject {15 @Published var currentPage: Int9 collapsed lines
16 let session = ARSession()17
18 init() { self.session.run() }19
20 func changePage() {21 self.currentPage += 1 // ui 状态和 session 绑定,逻辑轻22 self.session.pause()23 }24}对于 3 的例子
1class RecorderViewModel: @ObservableObject {2 @Published var state: String // 状态的拷贝3}4actor Recorder {5 var state: String = "idle"6 let viewModel: RecorderViewModel7
8 init(viewModel) { self.viewModel = viewModel } // 注入 viewModel9
10 func run() {11 await something.prepare() // 逻辑重12 await something.prepare()13 self.state = "running" // 后端状态14 await MainActor.run { self.viewModel.state = "running" } // ui 状态和后端状态可以在一块儿管理15 }24 collapsed lines
16}17
18struct RecorderView: View {19 @ObservedObject var viewModel: RecorderViewModel20 let recorder: Recorder21
22 init(recorder, viewModel) { // 注入23 self.recorder = recorder24 self.viewModel = viewModel25 }26
27 body {28 await self.recorder.run()29 }30}31
32class PageCoodinator: @ObservableObject {33 let recorder: Recorder34 let viewModel: RecorderViewModel35 init() {36 self.viewModel = RecorderViewModel()37 self.recorder = Recorder(self.viewModel)38 }39}另外一个(自发状态变化)的例子:
1class NetworkViewModel: @ObservableObject {2 @Published var connected: Int = 03}4
5actor Network {6 var connected: Int7 let viewModel: NetworkViewModel8
9 init(viewModel) { self.viewModel = viewModel; self.loop() }10
11 private func loop() {12 if ("new device found") {13 self.connected += 114 self.viewModel.connected += 115 }2 collapsed lines
16 }17}