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

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属性传递异步结果(推荐)

  1. 在ViewModel中添加专门存储方向数据的@Published属性:
@Published var napravlenieList: [String] = []
  1. 重写数据获取方法,去掉返回值,改为异步完成后更新属性:
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
    }
}
  1. 修改View中的绑定逻辑:
    • 删除View中的@State var napravlenie: [String] = [""]
    • 将第二个Picker的数据源改为viewModel.napravlenieList
    • 更新onChange回调:
    .onChange(of: selectedCourse) { newValue in
        viewModel.fetchNapravlenie(for: newValue)
    }
    

方案2:用Completion Handler处理异步结果

如果不想用@Published,可给方法添加完成回调:

  1. 修改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)
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:15:36