Swiftui 在懒洋洋的大头钉内的嵌套ForEach上撞车

Swiftui 在懒洋洋的大头钉内的嵌套ForEach上撞车,swiftui,swiftui-foreach,identifiable,Swiftui,Swiftui Foreach,Identifiable,我有两个嵌套的ForEachs在LazyHStack LazyHStack { ForEach(items) { item in ForEach(item.urls, id: \.self) { Text($0.absoluteString) } } } 此代码段可以编译,但会立即崩溃,并出现以下错误 致命错误:每个布局项只能出现一次:文件SwiftUI,第0行 我在网上读到,这可能是因为ForEach没有正确区分集合中的

我有两个嵌套的
ForEach
s在
LazyHStack

LazyHStack {
    ForEach(items) { item in
        ForEach(item.urls, id: \.self) {
            Text($0.absoluteString)
        }
    }
}
此代码段可以编译,但会立即崩溃,并出现以下错误

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

我在网上读到,这可能是因为
ForEach
没有正确区分集合中的元素,即使所有元素都是可识别的。在我的例子中,外部
ForEach
中的
都是可识别的,而内部
ForEach
则在可选
URL?
对象数组中循环。我试图使用其绝对字符串(我认为应该是唯一的)来识别
URL
,但没有成功

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

我应该补充一点,相同的代码片段可以很好地用于标准的
HStack
。如何解决此问题?

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

例如,您应该在扩展名中使用
self

另外,你无缘无故地一起使用了2个ForEach inside,我会说编码不好,我不会建议一起使用2个ForEach inside,这会导致系统和你的应用程序崩溃,使用1个ForEach!然而,以下是你的答案:




用户界面的具体布局取决于您,但这个答案创建了一个水平滚动的URL列表,这些URL作为每个项目的一部分垂直堆叠

工作代码:

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

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

    另一种可能是为每个元素设置唯一的ID。基本上,如果多个URL相同(因此具有相同的ID),那么
    LazyHStack
    存在一个问题,即ID并不都是唯一的。链接到类似的答案。这是另一种修复方法,不需要在以下两者之间使用
    VStack

    Text($0.absoluteString)
        .id(item.id.uuidString + ($0.id ?? ""))
    
    编辑以支持可选URL 数据结构(这里唯一的区别是
    URL
    URLItem
    替换,因此我们可以保留一个可选值):

    新示例数据:

    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: ""))
        ])
    ]
    
    这意味着我们现在可以有
    可识别的
    可选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

    您可能有多个
    absoluteString
    。请尝试
    var id:UUID{UUID()}
    我不认为这是一个问题,而应用程序在运行时是智能的。。。你想要实现什么?如果在每个S的中间没有一个堆栈,那么应用程序崩溃是有意义的,因为它不知道如何为每个WHLIN除了“@ MADIBM”的注释之外布置第二个,也可以用<代码> >标识<代码> URL。而不是
    \.id
    或将
    id
    参数保留为空。谢谢您的回答。嵌套堆栈对于我的用例来说是一个很好的解决方案,但是我仍然有问题,因为
    URL
    数组包含可选值。在这种情况下,编译器返回以下错误:“ForEach”上引用初始值设定项“init(uuz:content:)”要求“URL”符合“可识别”。@Dree您希望如何处理可选URL?您想不显示它们,显示一些替代文本,如“无URL”,还是以其他方式?你能保证这些URL不是零吗?例如,如果您只是不想显示
    nil
    URL,您可以在
    item.url
    之后使用
    .compactMap{$0}
    来删除
    nil
    值。如果
    文本
    的ID有问题,您可以改为执行
    .ID(UUID())
    。在
    ForEach
    中,不仅有一个简单的
    文本
    视图,还有另一个视图负责处理URL的nil情况。不幸的是,我不能仅仅使用
    compactMap
    删除nil值@Dree code现在更新为支持可选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: ""))
        ])
    ]
    
    ForEach(item.urls) {
        if let url = $0.url {
            Text(url.absoluteString)
                .id($0.id)
        } else {
            Text("Bad URL")
                .id($0.id)
        }
    }