微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使SwiftUI List / OutlineGroup变得懒惰以便与大型树如文件系统一起使用?

如何解决如何使SwiftUI List / OutlineGroup变得懒惰以便与大型树如文件系统一起使用?

这是SwiftUI中层次结构List的简单演示。我正在macOS Big Sur上对其进行测试,但与其他UI工具包中的类似树组件不同,它会立即要求其所有子代。因此,我不能将其用于文件系统浏览器之类的东西。

是否有一种使其变得懒惰的方法,以便仅在扩展UI元素时要求children

class Thing: Identifiable {
    let id: UUID
    let depth: Int
    let name: String
    init(_ name: String,depth: Int = 0) {
        self.id = UUID()
        self.name = name
        self.depth = depth
    }
    /// Lazy computed property
    var children: [Thing]? {
        if depth >= 5 { return nil }
        if _children == nil {
            print("Computing children property,name=\(name),depth=\(depth)")
            _children = (1...5).map { n in
                Thing("\(name).\(n)",depth:depth+1)
            }
        }
        return _children
    }
    private var _children: [Thing]? = nil
}

struct ContentView: View {
    var things: [Thing] = [Thing("1"),Thing("2"),Thing("3")]
    var body: some View {
        List(things,children: \.children) { thing in
            Text(thing.name)
        }
    }
}

即使初始用户界面仅显示顶部节点:

您可以在控制台中看到它要求所有内容-一直到树下。这是大树的性能问题。

...
Computing children property,name=3.4.4.1.4,depth=4
Computing children property,name=3.4.4.1.5,name=3.4.4.2,depth=3
Computing children property,name=3.4.4.2.1,name=3.4.4.2.2,depth=4
...

解决方法

我相信这可能是 SwiftUI 中的一个错误,我希望 Apple 能够解决这个问题。同时,您可以使用以下解决方法:

struct Node {
    var id: String
    var value: String
    var children: [Node]?
}

struct LazyDisclosureGroup: View {
    let node: Node
    @State var isExpanded: Bool = false

    var body: some View {
        if node.children != nil {
            DisclosureGroup(
                isExpanded: $isExpanded,content: {
                    if isExpanded {
                        ForEach(node.children!,id: \.self.id) { childNode in
                            LazyDisclosureGroup(node: childNode)
                        }
                    }
                },label: { Text(node.value) })
        } else {
            Text(node.value)
        }
    }
}

struct ContentView: View {
    let node = Node(
        id: "a",value: "a",children: [Node(id: "b",value: "b",children: nil),Node(id: "c",value: "c",children: nil)])
    var body: some View {
        List {
            LazyDisclosureGroup(node: node)
        }
    }
}

我不知道这是否是最佳实践,但解决方法使用了 DisclosureGroupList 内时“注意到”的观察结果。两者的结合产生相同的视觉结构和行为。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。