0

我在 a 中有两个嵌套ForEach的 sLazyHStack

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. 我怎么解决这个问题?

4

2 回答 2

1

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

工作代码:

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

变化:

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

结果:

结果

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

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

编辑以支持可选 URL

数据的结构(这里唯一的区别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)
    }
}

您现在可以处理自己的案件 where $0.urlis nil

于 2021-04-17T15:50:44.377 回答
-1

它正在工作,你在代码中有一些错误:

例如你应该self在你的扩展中使用

你也无缘无故地在里面一起使用了 2 个 ForEach,我会说不好的编码,我不建议在里面一起使用 2 个 ForEach,它会破坏系统和你的应用程序,使用 1 个 ForEach!但是,这是您的答案:


在此处输入图像描述


 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 }
}
于 2021-04-17T15:27:55.530 回答