问题描述
我在 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
。我该如何解决这个问题?
解决方法
UI 的确切布局取决于您,但此答案会创建一个水平滚动的 URL 列表,这些 URL 垂直堆叠为每个项目的一部分。
工作代码:
ScrollView(.horizontal) {
LazyHStack {
ForEach(items) { item in
VStack {
ForEach(item.urls) {
Text($0.absoluteString)
}
}
}
}
}
变化:
- 将
LazyHStack
包裹在ScrollView
中,因此屏幕外的项目可以滚动查看。 - 在
VStack
之间插入ForEach
,以确定每个item
的布局。 - 从第二个
id: \.self
中删除了ForEach
,因为您已经为 URL 创建了自定义id
。使用id: \.id
或不在id
中包含ForEach
参数。
结果:
另一种可能是为每个元素设置一个唯一的 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.url
为 nil
的情况。
它正在工作,您在代码中犯了一些错误:
例如,您应该在扩展程序
中使用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 }
}