SwiftUI:无法删除列表中的行

时间:2020-05-23 10:20:14

标签: list swiftui

我在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]

它有效(除非自然地,它显示所有行中的第一个玩家,并且删除所有玩家(=行)后出现相同的错误)

任何帮助都会很棒-谢谢!

2 个答案:

答案 0 :(得分: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中的错误还是故意的?

答案 1 :(得分:0)

问题是您没有在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,然后创建了一个包含多个Player的数组。在ForEach中,您可以直接使用players,因为Player已确认Identifiable协议。这很容易,因为您可以在ForEach循环中访问播放器对象,而不必访问所有带有索引的对象。在删除功能中,您只需将对象从数组中删除或为其添加新的内容。现在删除可以正常工作。

我已经从列表行中删除了一些代码,只是为了更轻松地重现它,即使您想知道。