1

当我尝试在 Text() 中显示选择器的选定数据时,会出现一个名为“索引超出范围”的错误。

错误消息的图像

但是,当我评论显示所选数据的 Text() 时,它工作正常。以下是表单中选择器的代码。

struct VMPickerView: View {
    
    @State var vmIndex = 0
    @ObservedObject var stockViewModel = StockViewModel()

    var body: some View {
        
        let allVM = self.stockViewModel.arrKey

        return VStack {
        
            Form {
                
                Section {
                    
                    Picker(selection: $vmIndex, label: Text("Location")) {
                        
                        ForEach(0..<allVM.count, id: \.self) {
                            
                            Text(allVM[$0]).tag($0)
                        }
                    }
                    //Text(allVM[vmIndex])
                }
            }
        }
    }
}

下面是我评论“Text(allVM[vmIndex])”时我的应用程序的图像

屏幕一

屏幕 2

下面是我用来从 firebase 检索数据并存储到数组中的代码。

class StockViewModel: ObservableObject {
    
    @Published var itemList = [ItemList]()
    @Published var arrKey = [String]()
    
    init() {
        retrieveAllVM()
    }
    
    func retrieveAllVM() {
        
        var arrKey = [String]()
       
        let ref = Database.database().reference().child("VM")

        ref.observeSingleEvent(of: .value, with: { snapshot in
            for items in snapshot.children {
                let itemSnap = items as! DataSnapshot
                let allKey = itemSnap.key
                arrKey.append(allKey)
            }
            self.arrKey = arrKey
            print(self.arrKey)
       })
    }
}

*更改后我的代码:

class StockViewModel: ObservableObject {
    
    @Published var itemList = [ItemList]()
    @Published var arrKey = [String]()
    
    func retrieveAllVM() {
        
        var arrKey = [String]()
       
        let ref = Database.database().reference().child("VM")

        ref.observeSingleEvent(of: .value, with: { snapshot in
            for items in snapshot.children {
                let itemSnap = items as! DataSnapshot
                let allKey = itemSnap.key
                arrKey.append(allKey)
            }
            DispatchQueue.main.async {
                self.arrKey = arrKey
                print(self.arrKey)
            }
            //self.arrKey = arrKey
       })
    }
}
struct VMPickerView: View {
    
    @State var vmIndex = 0
    @ObservedObject var stockViewModel: StockViewModel

    var body: some View {
        
        let allVM = self.stockViewModel.arrKey

        return VStack {
        
            Form {
                
                Section {
                    
                    Picker(selection: $vmIndex, label: Text("Location")) {
                        
                        ForEach(0..<allVM.count, id: \.self) {
                            
                            Text(allVM[$0]).tag($0)
                        }
                    }
                    //Text(allVM[vmIndex])
                }
            }
        }.onAppear {
            self.stockViewModel.retrieveAllVM()
        }
    }
}
4

1 回答 1

0

observeSingleEvent方法看起来是异步的。@Published确保在主线程上更新您的属性。

代替:

self.arrKey = arrKey

和:

DispatchQueue.main.async {
    self.arrKey = arrKey
}

您在 init 中的代码将在每次创建 ViewModel 时运行。

class StockViewModel: ObservableObject {
    ...
    init() {
        retrieveAllVM()
    }

您可以将呼叫retrieveAllVM移至.onAppear

struct VMPickerView: View {
    @State var vmIndex = 0
    @ObservedObject var stockViewModel = StockViewModel()

    var body: some View {
        let allVM = self.stockViewModel.arrKey

        return VStack {
            ...
        }.onAppear {
            self.stockViewModel.retrieveAllVM()
        }
    }
}

或者,不要直接在VMPickerView. 在父视图中创建 ViewModel 并将其传递给VMPickerView

struct VMPickerView: View {
    @State var vmIndex = 0
    @ObservedObject var stockViewModel: StockViewModel // pass only
    ...
}

或者,如果您使用的是 SwiftUI 2.0,您可以使用@StateObject

struct VMPickerView: View {
    @State var vmIndex = 0
    @StateObject var stockViewModel = StockViewModel()
    ...
}

编辑

处理指数是有风险的。如果由于任何其他原因您的代码失败,请尝试使用iforguard语句以确保您永远不会访问无效索引。

代替:

Form {
    Section {
        ...
        Text(allVM[vmIndex])
    }
}

您可以在选择器视图中添加一个返回当前键视图的计算属性:

@ViewBuilder
var currentKeyText: some View {
    if vmIndex < stockViewModel.arrKey.count {
        Text(stockViewModel.arrKey[vmIndex])
    }
}

并像这样访问它:

Form {
    Section {
        ...
        currentKeyText
    }
}
于 2020-07-29T18:20:04.087 回答