![](/img/trans.png)
[英]SwiftUI - Fatal error: index out of range on deleting element from an array
[英]SwiftUI out of index when deleting an array element in ForEach
我在這里查看了不同的問題,但不幸的是我找不到答案。 這是我的代碼:
SceneDelegate.swift
...
let contentView = ContentView(elementHolder: ElementHolder(elements: ["abc", "cde", "efg"]))
...
window.rootViewController = UIHostingController(rootView: contentView)
ContentView.swift
class ElementHolder: ObservableObject {
@Published var elements: [String]
init(elements: [String]) {
self.elements = elements
}
}
struct ContentView: View {
@ObservedObject var elementHolder: ElementHolder
var body: some View {
VStack {
ForEach(self.elementHolder.elements.indices, id: \.self) { index in
SecondView(elementHolder: self.elementHolder, index: index)
}
}
}
}
struct SecondView: View {
@ObservedObject var elementHolder: ElementHolder
var index: Int
var body: some View {
HStack {
TextField("...", text: self.$elementHolder.elements[self.index])
Button(action: {
self.elementHolder.elements.remove(at: self.index)
}) {
Text("delete")
}
}
}
}
按下刪除按鈕時,應用程序因索引越界錯誤而崩潰。
有兩個奇怪的事情,應用程序正在運行時
1)您刪除VStack
並將ForEach
ContentView.swift
body
2)你把SecondView
的代碼直接放到ForEach
只有一件事:我真的需要ObservableObject
,這段代碼只是另一個代碼的簡化。
更新
我更新了我的代碼並將Text
更改為TextField
,因為我不能只傳遞一個字符串,我需要雙向連接。
問題出在單擊刪除按鈕時執行更新的順序。
按下按鈕時,將發生以下情況:
elements
屬性被改變objectWillChange
發布者發送通知,該發布者是ElementHolder
的一部分,由ObservableObject
協議聲明。body
getter 更新其視圖。body
getter 更新其視圖。為了讓代碼不崩潰,3.1 必須在 3.2 之后執行。 盡管(據我所知)無法控制此順序。
最優雅的解決方案是在 SecondView 中創建一個onDelete
閉包,它將作為參數傳遞。
這也將解決元素視圖可以訪問所有元素的架構反模式,而不僅僅是它顯示的元素。
整合所有這些將產生以下代碼:
struct ContentView: View {
@ObservedObject var elementHolder: ElementHolder
var body: some View {
VStack {
ForEach(self.elementHolder.elements.indices, id: \.self) { index in
SecondView(
element: self.elementHolder.elements[index],
onDelete: {self.elementHolder.elements.remove(at: index)}
)
}
}
}
}
struct SecondView: View {
var element: String
var onDelete: () -> ()
var body: some View {
HStack {
Text(element)
Button(action: onDelete) {
Text("delete")
}
}
}
}
有了這個,甚至可以刪除 ElementHolder 並只擁有一個@State var elements: [String]
變量。
這是可能的解決方案 - 使SecondView
的主體不依賴於ObservableObject
。
用 Xcode 11.4 / iOS 13.4 測試 - 沒有崩潰
struct SecondView: View {
@ObservedObject var elementHolder: ElementHolder
var index: Int
let value: String
init(elementHolder: ElementHolder, index: Int) {
self.elementHolder = elementHolder
self.index = index
self.value = elementHolder.elements[index]
}
var body: some View {
HStack {
Text(value) // not refreshed on delete
Button(action: {
self.elementHolder.elements.remove(at: self.index)
}) {
Text("delete")
}
}
}
}
另一種可能的解決方案是不要在SecondView
中觀察ElementHolder
...不需要呈現和刪除它 - 也不會崩潰
struct SecondView: View {
var elementHolder: ElementHolder // just reference
var index: Int
var body: some View {
HStack {
Text(self.elementHolder.elements[self.index])
Button(action: {
self.elementHolder.elements.remove(at: self.index)
}) {
Text("delete")
}
}
}
}
更新:文本字段的SecondView
變體(僅更改的是文本字段本身)
struct SecondViewA: View {
var elementHolder: ElementHolder
var index: Int
var body: some View {
HStack {
TextField("", text: Binding(get: { self.elementHolder.elements[self.index] },
set: { self.elementHolder.elements[self.index] = $0 } ))
Button(action: {
self.elementHolder.elements.remove(at: self.index)
}) {
Text("delete")
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.