SwiftUI:无法删除列表中的行
Posted
技术标签:
【中文标题】SwiftUI:无法删除列表中的行【英文标题】:SwiftUI: cannot delete Row in List 【发布时间】:2020-05-23 10:20:14 【问题描述】:我在 Xcode 中有一个小的 swiftUI 程序,它让我可以在一个列表中创建和删除用户,并使用步进器来计算用户的点数。
除非删除用户,否则一切正常(添加用户、重命名用户、步进计数)。
它会抛出一个错误:
致命错误:索引超出范围:文件 /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.2.25.8/swift/stdlib/public/core/ContiguousArrayBuffer.swift, 第 444 行 2020-05-23 12:06:22.854920+0200 计数器[21328:1125981] 致命 错误:索引超出范围:文件 /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.2.25.8/swift/stdlib/public/core/ContiguousArrayBuffer.swift, 第 444 行
代码如下:
import SwiftUI
struct ContentView : View
@State var isEditing = false
@State var stepperWerte = [3, 5, 7, 9]
@State var editText = ["Player 1", "Player 2", "Player 3", "Player 4"]
var startName = "new Player"
var startLeben = 5
var body: some View
NavigationView
List()
ForEach(0..<editText.count, id: \.self)
spieler in HStack
if self.editText.indices.contains(spieler)
Stepper(value: self.$stepperWerte[spieler], in: -1...10, step: 1, label:
TextField("", text: self.$editText[spieler], onEditingChanged: _ in , onCommit: self.saveText(id: spieler, Text: self.editText[spieler]) )
.layoutPriority(1)
.fixedSize(horizontal: true, vertical: false)
Text("\(self.stepperWerte[spieler]) - \(spieler) - \(self.editText.count)"))
.onDelete(perform: spielerLoeschen)
.frame(width: nil, height: nil, alignment: .trailing)
.navigationBarTitle(Text("Nav_Title"))
.navigationBarItems(leading: Button(action: self.isEditing.toggle() ) Text(isEditing ? "Done" : "Edit").frame(width: 85, height: 40, alignment: .leading) ,
trailing: Button(action: spielerHinzufuegen, label: Image(systemName: "plus") ) )
.environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive)).animation(Animation.spring())
func spielerLoeschen(at offsets: IndexSet)
stepperWerte.remove(atOffsets: offsets)
editText.remove(atOffsets: offsets)
func spielerHinzufuegen()
stepperWerte.append(startLeben)
editText.append(startName)
func saveText(id: Int, Text: String)
editText[id] = Text
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
(忽略HStack后面的“if”,它没有实际效果,最后一个Text中的那些额外打印显示索引和总数)
如果我转储数组(stepperWerte 和 editText),它们会以正确的方式删除 -> 选择删除的玩家将从两个数组中正确删除。
如果我改变了
TextField("", text: self.$editText[spieler]
到
TextField("", text: self.$editText[0]
它可以工作(除非它自然地显示所有行中的第一个玩家,并且在删除所有玩家(=rows)后我得到了同样的错误)
任何帮助都会很棒 - 谢谢!
【问题讨论】:
问题解释及解决方法见here 【参考方案1】:根据@Asperi,我已将代码更改为以下内容: 导入 SwiftUI
struct BetterTextField : View
var container: Binding<[String]>
var index: Int
@State var text: String
var body: some View
TextField("", text: self.$text, onCommit:
self.container.wrappedValue[self.index] = self.text
)
.layoutPriority(1)
.fixedSize(horizontal: true, vertical: false)
struct ContentView : View
@State var isEditing = false
@State var stepperWerte = [3, 5, 7, 9]
@State var editText = ["Player 1", "Player 2", "Player 3", "Player 4"]
var startName = "new Player"
var startLeben = 5
var body: some View
NavigationView
List()
ForEach(0..<editText.count, id: \.self)
spieler in HStack
if self.editText.indices.contains(spieler)
Stepper(value: self.$stepperWerte[spieler], in: -1...10, step: 1, label:
BetterTextField(container: self.$editText, index: self.editText.firstIndex(of: self.editText[spieler])!, text: self.editText[spieler])
Text("\(self.stepperWerte[spieler]) - \(spieler) - \(self.editText.count)"))
.onDelete(perform: spielerLoeschen)
.frame(width: nil, height: nil, alignment: .trailing)
.navigationBarTitle(Text("Nav_Title"))
.navigationBarItems(leading: Button(action: self.isEditing.toggle() ) Text(isEditing ? "Done" : "Edit").frame(width: 85, height: 40, alignment: .leading) ,
trailing: Button(action: spielerHinzufuegen, label: Image(systemName: "plus") ) )
.environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive)).animation(Animation.spring())
func spielerLoeschen(at offsets: IndexSet)
stepperWerte.remove(atOffsets: offsets)
editText.remove(atOffsets: offsets)
func spielerHinzufuegen()
stepperWerte.append(startLeben)
editText.append(startName)
func saveText(id: Int, Text: String)
editText[id] = Text
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
...它有效 - 谢谢!
但是: 这是 SwiftUI 中的错误还是故意的?
【讨论】:
【参考方案2】:问题是您没有直接在 ForEach 循环中使用您的项目。考虑将数据结构用作对象并使其可识别。
struct Player : Identifiable
let id = UUID()
var stepperWerte: Int
var editText : String
struct ContentView : View
@State var isEditing = false
@State var players = [Player(stepperWerte: 3, editText: "Player 1"), Player(stepperWerte: 5, editText: "Player 2"), Player(stepperWerte: 7, editText: "Player 3"), Player(stepperWerte: 9, editText: "Player 4")]
var startName = "new Player"
var startLeben = 5
var body: some View
NavigationView
List()
ForEach(self.players) player in
SecondView(player: player)
.onDelete(perform: spielerLoeschen)
.frame(width: nil, height: nil, alignment: .trailing)
.navigationBarTitle(Text("Nav_Title"))
.navigationBarItems(leading: Button(action: self.isEditing.toggle() ) Text(isEditing ? "Done" : "Edit").frame(width: 85, height: 40, alignment: .leading) ,
trailing: Button(action: spielerHinzufuegen, label: Image(systemName: "plus") ) )
.environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive)).animation(Animation.spring())
func spielerLoeschen(at offsets: IndexSet)
players.remove(atOffsets: offsets)
func spielerHinzufuegen()
players.insert(Player(stepperWerte: 4, editText: "Neuer Player"), at: 0)
struct SecondView : View
var player : Player
@State var stepperWerte : Int
@State var name : String
init(player : Player)
self._stepperWerte = State(initialValue: player.stepperWerte)
self._name = State(initialValue: player.editText)
self.player = player
var body: some View
Stepper(value: self.$stepperWerte, in: -1...10, step: 1, label:
TextField("", text: self.$name)
.layoutPriority(1)
.fixedSize(horizontal: true, vertical: false)
Text("\(player.stepperWerte)")
)
我创建了一个结构Player
,然后创建了一个包含许多玩家的数组。在 ForEach 中,您可以直接使用 players
作为 Player
确认到 Identifiable
协议。这更容易,因为您可以在 ForEach 循环中访问播放器对象,而不必使用索引访问所有内容。在删除函数中,您只需从数组中删除对象或添加新对象。现在删除工作正常。
我从列表行中删除了一些代码,只是为了更容易重现它,如果您想知道的话。
【讨论】:
这也很好,我选择了这个,因为它对我来说更容易理解。谢谢! 不知何故,玩家名称的编辑和步进器的值没有改变/保存。这在您的版本中有效吗? @ma2412 我已经更新了代码。添加作品,并更改名称。我将它外包给第二个视图并更改了结构。 Sollte funktionieren ;)以上是关于SwiftUI:无法删除列表中的行的主要内容,如果未能解决你的问题,请参考以下文章
如果列表行在 SwiftUI 中包含文本字段,则无法从 foreach 中删除元素
SwiftUI:Firebase ForEach 列表执行编辑功能无法编辑选定的行项目
如何从 Mac OS 中的 SwiftUI 列表中删除底部/顶部项目填充
SwiftUI 试图从列表中删除对象但无法在不可变值上使用变异成员:是一个“让”常量