Swift中Picker的onChange延迟更新,Firebase数据同步异常求助
问题:Picker选择后数组延迟更新的解决办法
问题场景
我是Swift新手,当前遇到一个问题:操作第一个Picker时,第二个Picker的数据源napravlenie数组无法立即更新,第二次触发时才会显示第一次应该加载的结果。需求是选择第一个Picker后,第二个Picker能立刻更新对应选项。
相关代码
View代码
@State var selectedCourse: String = "1" @State var napravlenie: [String] = [""] var body: some View { NavigationView { ZStack{ VStack{ Text("Выберите вашу группу") .padding(.bottom,80) .font(.system(size: 30, weight: .semibold)) Text("Курс") .font(.system(size: 28, weight: .bold)) .frame(width: 370,alignment: .leading) Menu { Picker(selection: $selectedCourse, label: EmptyView()) { ForEach(course, id: \.self) {option in Text (option) .tag (1) } } } label: { customLabel_1 } .onChange(of: selectedCourse) { newValue in napravlenie = viewModel.readValue(kyrs: newValue) } Text("Направление") .font(.system(size: 28, weight: .bold)) .frame(width: 370,alignment: .leading) .padding(.bottom,28) Menu { Picker(selection: $selectedNapravlenie, label: EmptyView()) { ForEach(napravlenie, id: \.self) {option in Text (option) .tag (2) } } } label: { customLabel_2 } } } }
ViewModel代码
import Foundation import FirebaseCore import FirebaseDatabase class userViewModel: ObservableObject{ @Published var key = "" @Published var moreKey = [] @Published var key2 = "" @Published var moreKey2 = [] @Published var nameKey2 = "" @Published var nameKey3 = "" @Published var object: String? = nil @Published var userName: Any? = [] var ref = Database.database().reference() func readValue(kyrs:String) -> [String]{ ref.child("Курс - " + kyrs).observeSingleEvent(of: .value) { snapshot in for child in snapshot.children{ let snap = child as! DataSnapshot self.key = snap.key self.moreKey.append(self.key) print(self.moreKey) } } let newArray: [String] = self.moreKey.compactMap { String(describing: $0) } self.moreKey.removeAll() return newArray } }
问题原因
核心问题出在readValue方法的异步逻辑上:Firebase的observeSingleEvent是异步操作,会在后台完成数据读取后才执行闭包,但你的方法在闭包执行前就直接返回了newArray——此时moreKey要么是空数组,要么是上一次请求的残留数据。第二次触发时,上一次异步请求完成的结果刚好被返回,所以出现延迟更新的现象。
解决方案
方案1:用@Published属性传递异步结果(推荐)
- 在ViewModel中添加专门存储方向数据的@Published属性:
@Published var napravlenieList: [String] = []
- 重写数据获取方法,去掉返回值,改为异步完成后更新属性:
func fetchNapravlenie(for kyrs: String) { napravlenieList.removeAll() // 清空旧数据 ref.child("Курс - " + kyrs).observeSingleEvent(of: .value) { snapshot in var tempArray: [String] = [] for child in snapshot.children { let snap = child as! DataSnapshot tempArray.append(snap.key) } self.napravlenieList = tempArray } }
- 修改View中的绑定逻辑:
- 删除View中的
@State var napravlenie: [String] = [""] - 将第二个Picker的数据源改为
viewModel.napravlenieList - 更新
onChange回调:
.onChange(of: selectedCourse) { newValue in viewModel.fetchNapravlenie(for: newValue) } - 删除View中的
方案2:用Completion Handler处理异步结果
如果不想用@Published,可给方法添加完成回调:
- 修改ViewModel的方法:
func readValue(kyrs: String, completion: @escaping ([String]) -> Void) { ref.child("Курс - " + kyrs).observeSingleEvent(of: .value) { snapshot in var tempArray: [String] = [] for child in snapshot.children { let snap = child as! DataSnapshot tempArray.append(snap.key) } completion(tempArray) } }
- 在View中通过回调更新数组:
.onChange(of: selectedCourse) { newValue in viewModel.readValue(kyrs: newValue) { result in DispatchQueue.main.async { self.napravlenie = result } } }
额外优化点
- Picker的
tag设置错误:原代码中所有选项的tag都是固定值(1或2),导致Picker无法正确绑定选中值,应改为用选项本身作为tag:
// 第一个Picker ForEach(course, id: \.self) { option in Text(option) .tag(option) } // 第二个Picker ForEach(viewModel.napravlenieList, id: \.self) { option in Text(option) .tag(option) }
- ViewModel中的
moreKey应指定类型为[String],避免Any类型带来的隐患:@Published var moreKey: [String] = []
内容的提问来源于stack exchange,提问作者eccclips
相关产品推荐
相关产品推荐

