SwiftUI Picker desn';不要与观察对象绑定
我试图用从外部API异步获取的数据填充选择器 这是我的模型:SwiftUI Picker desn';不要与观察对象绑定,swiftui,bind,picker,observedobject,Swiftui,Bind,Picker,Observedobject,我试图用从外部API异步获取的数据填充选择器 这是我的模型: struct AppModel: Identifiable { var id = UUID() var appId: String var appBundleId : String var appName: String var appSKU: String } 获取数据并发布的类是: class AppViewModel: ObservableObject { private var
struct AppModel: Identifiable {
var id = UUID()
var appId: String
var appBundleId : String
var appName: String
var appSKU: String
}
获取数据并发布的类是:
class AppViewModel: ObservableObject {
private var appStoreProvider: AppProvider? = AppProvider()
@Published private(set) var listOfApps: [AppModel] = []
@Published private(set) var loading = false
fileprivate func fetchAppList() {
self.loading = true
appStoreProvider?.dataProviderAppList { [weak self] (appList: [AppModel]) in
guard let self = self else {return}
DispatchQueue.main.async() {
self.listOfApps = appList
self.loading = false
}
}
}
init() {
fetchAppList()
}
}
意见是:
struct AppView: View {
@ObservedObject var appViewModel: AppViewModel = AppViewModel()
@State private var selectedApp = 0
var body: some View {
ActivityIndicatorView(isShowing: self.appViewModel.loading) {
VStack{
// The Picker doesn't bind with appViewModel
Picker(selection: self.$selectedApp, label: Text("")) {
ForEach(self.appViewModel.listOfApps){ app in
Text(app.appName).tag(app.appName)
}
}
// The List correctly binds with appViewModel
List {
ForEach(self.appViewModel.listOfApps){ app in
Text(app.appName.capitalized)
}
}
}
}
}
}
当列表视图与观察到的对象appViewModel绑定时,选择器的行为方式不同。我不知道为什么。有什么帮助吗 我无法将其放入您的代码中,因为它不完整,但这里有一个示例。 选择器不是动态的。它们必须完全重新加载
class DynamicPickerViewModel: ObservableObject {
@Published private(set) var listOfApps: [YourModel] = []
@Published private(set) var loading = false
fileprivate func fetchAppList() {
loading = true
DispatchQueue.main.async() {
self.listOfApps.append(YourModel.addSample())
self.loading = false
}
}
init() {
fetchAppList()
}
}
struct DynamicPicker: View {
@ObservedObject var vm = DynamicPickerViewModel()
@State private var selectedApp = ""
var body: some View {
VStack{
//Use your loading var to reload the picker when it is done
if !vm.loading{
//Picker is not meant to be dynamic, it needs to be completly reloaded
Picker(selection: self.$selectedApp, label: Text("")) {
ForEach(self.vm.listOfApps){ app in
Text(app.name!).tag(app.name!)
}
}
}//else - needs a view while the list is being loaded/loading = true
List {
ForEach(self.vm.listOfApps){ app in
Text(app.name!.capitalized)
}
}
Button(action: {
self.vm.fetchAppList()
}, label: {Text("fetch")})
}
}
}
struct DynamicPicker_Previews: PreviewProvider {
static var previews: some View {
DynamicPicker()
}
}
我提交了错误报告FB7670992。苹果昨天做出回应,建议我在iOS 14 beta 1中确认这一行为。现在似乎已经解决了
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
Picker("", selection: $viewModel.wheelPickerValue) {
ForEach(viewModel.objects) { object in
Text(object.string)
}
}
.pickerStyle(WheelPickerStyle())
.labelsHidden()
}
}
在哪里
struct对象:可识别{
设id=UUID().UUIString
让字符串:字符串
}
类ViewModel:ObservableObject{
专用变量计数器=0
@已发布的专用(集)变量对象:[Object]=[]
@已发布的var segmentedPickerValue:String=“”
@已发布的var wheelPickerValue:String=“”
fileprivate func nextSetOfValues(){
设newCounter=counter+3
对象=(如果你给我们一个可编辑的可复制的例子……我们会帮助你……但是你的代码甚至不能编译,因为你只发布了部分代码。请阅读并考虑这个:是的!!非常感谢!!(或者标记类型,如果您要像这里一样显式指定标记)。有趣的是,如果您使用.pickerStyle(SegmentedPickerStyle())
,它确实会得到更新。(我确定您不需要分段选择器,所以这不是问题的答案,但它只是一个有趣的数据点。)感觉像是PickerView
的updateUI
缺少一个reloadAllComponents
…FWIW,我提交了错误报告,FB7670992.FWIW,这种技术只有在fetchAppList
实际需要一点时间的情况下才有效。例如,在您的示例中,DispatchQueue.main.async
是不够的,但类似于DispatchQueue.main.asyncAfter(截止日期:.now()+0.1)
是。我不确定我是否会说选择器“注定”不是动态的。这似乎只是一个疏忽/错误(因为为什么它会与分段选择器而不是轮式选择器一起工作)。
struct Object: Identifiable {
let id = UUID().uuidString
let string: String
}
class ViewModel: ObservableObject {
private var counter = 0
@Published private(set) var objects: [Object] = []
@Published var segmentedPickerValue: String = ""
@Published var wheelPickerValue: String = ""
fileprivate func nextSetOfValues() {
let newCounter = counter + 3
objects = (counter..<newCounter).map { value in Object(string: "\(value)") }
let id = objects.first?.id ?? ""
segmentedPickerValue = id
wheelPickerValue = id
counter = newCounter
}
init() {
let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
guard let self = self else { timer.invalidate(); return }
self.nextSetOfValues()
}
timer.fire()
}
}