在 LazyHStack 中嵌套 ForEach 时崩溃

时间:2021-04-17 14:56:58

标签: swiftui swiftui-foreach identifiable

我在 ForEach 中有两个嵌套的 LazyHStack

LazyHStack {
    ForEach(items) { item in
        ForEach(item.urls, id: \.self) {
            Text($0.absoluteString)
        }
    }
}

此代码段可以编译,但立即崩溃并出现以下错误

<块引用>

致命错误:每个布局项只能出现一次:文件 SwiftUI,第 0 行

我在网上读到这可能是由于 ForEach 没有正确区分集合的元素,即使所有元素都可以识别。就我而言,外部 items 中的 ForEach 都是可识别的,而内部 ForEach 循环遍历可选 URL? 对象数组。我试图使 URL 使用其绝对字符串(我认为应该是唯一的)可识别,但没有奏效。

extension URL: Identifiable {
    var id: String? { absoluteString } 
}

我应该补充一点,相同的代码片段适用于标准的 HStack。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

UI 的确切布局取决于您,但此答案会创建一个水平滚动的 URL 列表,这些 URL 垂直堆叠为每个项目的一部分。

工作代码:

ScrollView(.horizontal) {
    LazyHStack {
        ForEach(items) { item in
            VStack {
                ForEach(item.urls) {
                    Text($0.absoluteString)
                }
            }
        }
    }
}

变化:

  1. LazyHStack 包裹在 ScrollView 中,因此屏幕外的项目可以滚动查看。
  2. VStack 之间插入 ForEach,以确定每个 item 的布局。
  3. 从第二个 id: \.self 中删除了 ForEach,因为您已经为 URL 创建了自定义 id。使用 id: \.id 或不在 id 中包含 ForEach 参数。

结果:

Result

另一种可能是为每个元素设置一个唯一的 ID。基本上,如果多个 URL 相同(因此具有相同的 ID),则 LazyHStack 存在 ID 并非都是唯一的问题。链接到类似的答案 here。这是不需要 VStack 之间的替代修复:

Text($0.absoluteString)
    .id(item.id.uuidString + ($0.id ?? ""))

编辑以支持可选网址

数据结构(唯一的区别是 URL 被替换为 URLItem,因此我们可以保存一个可选值):

struct Item: Identifiable {
    let id = UUID()
    let urls: [URLItem]
}

struct URLItem: Identifiable {
    let id = UUID()
    let url: URL?
    
    init(_ url: URL?) {
        self.url = url
    }
}

新示例数据:

let items: [Item] = [
    Item(urls: [
        URLItem(URL(string: "https://www.google.com")), URLItem(URL(string: "https://www.stackoverflow.com"))
    ]),
    Item(urls: [
        URLItem(URL(string: "https://www.stackoverflow.com")), URLItem(URL(string: ""))
    ])
]

这意味着我们现在可以拥有 Identifiable 可选 URL。代码现在应如下所示:

ForEach(item.urls) {
    if let url = $0.url {
        Text(url.absoluteString)
            .id($0.id)
    } else {
        Text("Bad URL")
            .id($0.id)
    }
}

您现在可以自行处理 $0.urlnil 的情况。

答案 1 :(得分:-1)

它正在工作,您在代码中犯了一些错误:

例如,您应该在扩展程序

中使用 self

你也无缘无故地在内部使用了 2 个 ForEach,我会说编码不好,我不建议在内部使用 2 个 ForEach,它会导致系统和您的应用程序崩溃,请使用 1 个 ForEach!但是,这是您的答案:


enter image description here


 struct ContentView: View {
    
    @State private var items: [[URL]] = [[URL(string: "www.apple.com")!, URL(string: "www.amazon.com")!, URL(string: "www.google.com")!], [URL(string: "www.Tesla.com")!]]

    var body: some View {

        LazyHStack {
            
            ForEach(items, id: \.self) { item in
                
                ForEach(item) { url in
                    Text(url.absoluteString)
                }
                
            }
            
        }
        
        
    }
    
}

extension URL: Identifiable {
    public var id: String { self.absoluteString }
}