表单中的 SwiftUI 选择器 - 索引超出范围
Posted
技术标签:
【中文标题】表单中的 SwiftUI 选择器 - 索引超出范围【英文标题】:SwiftUI Picker in Form - Index out of Range 【发布时间】:2020-07-29 18:00:04 【问题描述】:当我尝试在 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])”时我的应用程序的图像
以下是我用来从 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()
【问题讨论】:
【参考方案1】: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。在父视图中创建 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()
...
编辑
处理指数是有风险的。如果由于任何其他原因您的代码失败,请尝试使用if
或guard
语句来确保您永远不会访问无效索引。
代替:
Form
Section
...
Text(allVM[vmIndex])
您可以在选择器视图中添加一个计算属性,返回当前键视图:
@ViewBuilder
var currentKeyText: some View
if vmIndex < stockViewModel.arrKey.count
Text(stockViewModel.arrKey[vmIndex])
并像这样访问它:
Form
Section
...
currentKeyText
【讨论】:
结果好像还是一样。 我尝试了前 3 个解决方案,最后一个解决方案不适用于我,因为我没有使用 SwiftUI 2.0。 我应用了您的解决方案,但结果仍然相同。我打印了从数组中获得的数据 --> ["Bukit Jalil", "Sri Petaling"],我认为该数组没有任何问题。 @BingoLingo 您的视图或数组本身没有问题。我在没有 firebase 的情况下运行了您的代码,并且运行良好。一般规则是将@Published
属性的每次更新都包含在DispatchQueue.main.async
中(如果它正在后台更新)。最好不要依赖 ViewModel 初始化 - SwiftUI 可能会刷新视图并重新加载您的 ViewModel - 这将导致 retrieveAllVM
触发。在视图中使用.onAppear
。
我使用 .onAppear 来调用 func 而不是 init() 但结果是一样的。以上是关于表单中的 SwiftUI 选择器 - 索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章
在 SwiftUI 中符合 RandomAccessCollection 时,下标中的索引超出范围