得到“致命错误:索引超出范围”:在 swiftui 的列表项中显示索引

foo*_*ear 2 list indices swiftui

更新:错误:类型'_'没有成员'1',如果在list-foreach中放置一个if闭包(if !self.showMarkedOnly || name.marked {}),为什么?

代码版本 4:

struct Name: Identifiable, Hashable {
    var id: String = UUID().uuidString
    var name: String
    var marked: Bool
    init(_ name: String, marked: Bool = false) { self.name = name; self.marked = marked }
}

struct TestView: View {
    @State private var list: [Name] = [Name("test1"), Name("test2"), Name("test3", marked: true), Name("test4"), Name("test5", marked: true), Name("test6"), Name("test7"), Name("test8")]
    @State private var showMarkedOnly = false

    var body: some View {
        VStack{
            Toggle(isOn: $showMarkedOnly) { Text("show marked only") }
            List {
                ForEach(Array(zip(0..., list)), id: \.1.id) { index, name in
//                    if !self.showMarkedOnly || name.marked {
                        HStack {
                            Text("\(index)").foregroundColor(name.marked ? .red : .gray)
                            Spacer()
                            Text("\(name.name)")
                        }
                        .background(Color.gray.opacity(0.001))
                        .onTapGesture {
                            self.list.remove(at: index)
                        }
//                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

==========

更新:我发现代码版本 2 的问题,我必须为 ForEach 提供一个 id。并更新了代码版本 2。

我找到了一种显示索引的优雅方式,它避免了 self.list[index]。但是我发现在一些复杂的代码中出现了错误“Type '_' has no member '1'”。

代码版本 3:

var body: some View {
        List {
            ForEach(Array(zip(0..., list)), id: \.1.id) { index, name in // a error occurs in some complex code: "Type '_' has no member '1'"
                HStack {
                    Text("\(index)")
                    Spacer()
                    Text("\(name.name)")
                }
                .background(Color.gray.opacity(0.001))
                .onTapGesture {
                    self.list.remove(at: index)
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我显示一个列表,然后删除我点击的项目。这是代码版本 1,它工作正常。当我使用索引(代码版本 2)将索引添加到列表项时,点击后出现“致命错误:索引超出范围”。

代码版本 1:

struct Name: Identifiable, Hashable {
    var id: String = UUID().uuidString
    var name: String
    init(_ name: String) { self.name = name }
}

struct TestView: View {
    @State private var list: [Name] = [Name("test1"), Name("test2"), Name("test3"), Name("test4"), Name("test5"), Name("test6"), Name("test7"), Name("test8")]

    var body: some View {
        List {
            ForEach(list) { name in
                HStack {
                    Text("\(0)")
                    Spacer()
                    Text("\(name.name)")
                }
                .background(Color.gray.opacity(0.001))
                .onTapGesture {
                    self.list = self.list.filter { $0 != name }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

代码版本 2:

struct TestView: View {
    @State private var list: [Name] = [Name("test1"), Name("test2"), Name("test3"), Name("test4"), Name("test5"), Name("test6"), Name("test7"), Name("test8")]

    var body: some View {
        List {
            //ForEach(list.indices) { index in
            ForEach(list.indices, id: \.self) { index in
                HStack {
                    Text("\(index)")
                    Spacer()
                    Text("\(self.list[index].name)")
                }
                .background(Color.gray.opacity(0.001))
                .onTapGesture {
                    self.list.remove(at: index)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

use*_*734 8

@State 是属性包装器,它将强制定义它的视图重新计算其主体。

在您的情况下,如果您删除索引处的项目,

HStack {
    Text("\(index)")
    Spacer()
    Text("\(self.list[index].name)")
}
.background(Color.gray.opacity(0.001))
.onTapGesture {
     self.list.remove(at: index)
 }
Run Code Online (Sandbox Code Playgroud)

HStack 中的文本

Text("\(self.list[index].name)")
Run Code Online (Sandbox Code Playgroud)

崩溃,只是因为 list[index] 不再存在。

使用

ForEach(list.indices, id:\.self) { index in ... }
Run Code Online (Sandbox Code Playgroud)

代替

ForEach(list.indices) { index in ... }
Run Code Online (Sandbox Code Playgroud)

将强制 SwiftUI 重新创建 TestView(请参阅 ForEach 构造函数中的 id:\.self)

SwiftUI 将在使用包裹在 @State 属性包装器中的属性的新值时制作新的 TestView 副本。

更新

请不要更新您的问题...

你的最后一个代码版本 4 一团糟,所以我把它改写成你可以复制 - 粘贴 - 运行的东西

import SwiftUI

struct Name: Identifiable, Hashable {
    var id: String = UUID().uuidString
    var name: String
    var marked: Bool
    init(_ name: String, marked: Bool = false) { self.name = name; self.marked = marked }
}

struct ContentView: View {
    @State private var list: [Name] = [Name("test1"), Name("test2"), Name("test3", marked: true), Name("test4"), Name("test5", marked: true), Name("test6"), Name("test7"), Name("test8")]
    @State private var showMarkedOnly = false

    var body: some View {
        VStack{
            Toggle(isOn: $showMarkedOnly) {
                Text("show marked only")
            }.padding(.horizontal)
            List {
                ForEach(Array(zip(0..., list)).filter({!self.showMarkedOnly || $0.1.marked}), id: \.1.id) { index, name in
                    HStack {
                        Text("\(index)").foregroundColor(name.marked ? .red : .gray)
                        Spacer()
                        Text("\(name.name)")
                    }
                    .background(Color.gray.opacity(0.001))
                    .onTapGesture {
                        self.list.remove(at: index)
                    }
                }
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Run Code Online (Sandbox Code Playgroud)

它应该看起来像 在此处输入图片说明

根据讨论更新

ForEach 不同版本的构造函数在内部使用不同的 ViewBuilder 功能

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ViewBuilder {

    /// Provides support for "if" statements in multi-statement closures, producing an `Optional` view
    /// that is visible only when the `if` condition evaluates `true`.
    public static func buildIf<Content>(_ content: Content?) -> Content? where Content : View

    /// Provides support for "if" statements in multi-statement closures, producing
    /// ConditionalContent for the "then" branch.
    public static func buildEither<TrueContent, FalseContent>(first: TrueContent) -> _ConditionalContent<TrueContent, FalseContent> where TrueContent : View, FalseContent : View

    /// Provides support for "if-else" statements in multi-statement closures, producing
    /// ConditionalContent for the "else" branch.
    public static func buildEither<TrueContent, FalseContent>(second: FalseContent) -> _ConditionalContent<TrueContent, FalseContent> where TrueContent : View, FalseContent : View
}
Run Code Online (Sandbox Code Playgroud)

这是关于“实现细节”,希望它会在下一个版本中记录。SwiftUI 还处于非常早期的开发阶段,我们必须小心。

让我们尝试强制 SwiftUI 遵循我们自己的方式!第一个单独的 RowView

struct RowView: View {
    var showMarkedOnly: Bool
    var index: Int
    var name: Name
    //@ViewBuilder
    var body: some View {
        if !self.showMarkedOnly || name.marked {
            HStack {
                Text(verbatim: index.description).foregroundColor(name.marked ? .red : .gray)
                Spacer()
                Text(verbatim: name.name)
            }
            .background(Color.gray.opacity(0.001))

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨

Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
Run Code Online (Sandbox Code Playgroud)

取消注释包裹我们身体的行

struct RowView: View {
    var showMarkedOnly: Bool
    var index: Int
    var name: Name
    @ViewBuilder
    var body: some View {
        if !self.showMarkedOnly || name.marked {
            HStack {
                Text(verbatim: index.description).foregroundColor(name.marked ? .red : .gray)
                Spacer()
                Text(verbatim: name.name)
            }
            .background(Color.gray.opacity(0.001))

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以按照您喜欢的方式使用代码:-)

struct ContentView: View {
    @State private var list: [Name] = [Name("test1"), Name("test2"), Name("test3", marked: true), Name("test4"), Name("test5", marked: true), Name("test6"), Name("test7"), Name("test8")]
    @State private var showMarkedOnly = false

    var body: some View {
        VStack{
            Toggle(isOn: $showMarkedOnly) {
                Text("show marked only")
            }.padding(.horizontal)
            List {
                ForEach(Array(zip(0..., list)), id: \.1.id) { (index, name) in
                    RowView(showMarkedOnly: self.showMarkedOnly, index: index, name: name).onTapGesture {
                    self.list.remove(at: index)
                }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最终结果使用 nowbuildIf<Content>构造,所有代码再次运行(结果看起来与上图完全相同)