Список или OutlineGroup по умолчанию расширен в SwiftUI

С помощью List или OutlineGroup в SwiftUI, как сделать некоторые (или все) их ветки расширенными по умолчанию при создании представления. Кажется, это возможно с DisclosureGroup с привязкой.

Это может быть полезно для восстановления состояния или настройки представления для презентации.

4 ответа

Решение

Многоразовая версия OutlineGroup, где возможность расширения находится под контролем.

      import SwiftUI

struct NodeOutlineGroup<Node>: View where Node: Hashable, Node: Identifiable, Node: CustomStringConvertible{
    let node: Node
    let childKeyPath: KeyPath<Node, [Node]?>
    @State var isExpanded: Bool = true

    var body: some View {
        if node[keyPath: childKeyPath] != nil {
            DisclosureGroup(
                isExpanded: $isExpanded,
                content: {
                    if isExpanded {
                        ForEach(node[keyPath: childKeyPath]!) { childNode in
                            NodeOutlineGroup(node: childNode, childKeyPath: childKeyPath, isExpanded: isExpanded)
                        }
                    }
                },
                label: { Text(node.description) })
        } else {
            Text(node.description)
        }
    }
}

struct ContentView: View {
    var body: some View {
        List {
            NodeOutlineGroup(node: data, childKeyPath: \.children, isExpanded: true)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct FileItem: Hashable, Identifiable, CustomStringConvertible {
    var id: Self { self }
    var name: String
    var children: [FileItem]? = nil
    var description: String {
        switch children {
        case nil:
            return "📄 \(name)"
        case .some(let children):
            return children.isEmpty ? "📂 \(name)" : "📁 \(name)"
        }
    }
}

let data =
  FileItem(name: "users", children:
    [FileItem(name: "user1234", children:
      [FileItem(name: "Photos", children:
        [FileItem(name: "photo001.jpg"),
         FileItem(name: "photo002.jpg")]),
       FileItem(name: "Movies", children:
         [FileItem(name: "movie001.mp4")]),
          FileItem(name: "Documents", children: [])
      ]),
     FileItem(name: "newuser", children:
       [FileItem(name: "Documents", children: [])
       ])
    ])

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Очень приятно, Пол Б., спасибо. Но я хотел бы иметь больше контроля над отображаемыми строками. Поэтому я немного расширил ваше решение:

      struct NodeOutlineGroup<Node, Content>: View where Node: Hashable, Node: Identifiable, Node: CustomStringConvertible, Content: View {
    let node: Node
    let childKeyPath: KeyPath<Node, [Node]?>
    @State var isExpanded: Bool = true
    let content: (Node) -> Content
    
    var body: some View {
        if node[keyPath: childKeyPath] != nil {
            DisclosureGroup(
                isExpanded: $isExpanded,
                content: {
                    if isExpanded {
                        ForEach(node[keyPath: childKeyPath]!) { childNode in
                            NodeOutlineGroup(node: childNode, childKeyPath: childKeyPath, isExpanded: isExpanded, content: content)
                        }
                    }
                },
                label: { content(node) })
        } else {
            content(node)
        }
    }
}

с использованием:

      struct ContentView: View {
    var body: some View {
        List {
            NodeOutlineGroup(node: data, childKeyPath: \.children, isExpanded: true) { node in
                Text(node.description)
            }
        }
    }
}

Я тоже искал это и считаю, что OutlineGroup этого не поддерживает. Вместо этого я перешел в DisclosureGroup, который OutlineGroup использует для своей реализации и напрямую поддерживает логическую привязку расширения, а также разрешает вложение:

      struct ToggleStates {
    var oneIsOn: Bool = false
    var twoIsOn: Bool = true
}
@State private var toggleStates = ToggleStates()
@State private var topExpanded: Bool = true

var body: some View {
    DisclosureGroup("Items", isExpanded: $topExpanded) {
        Toggle("Toggle 1", isOn: $toggleStates.oneIsOn)
        Toggle("Toggle 2", isOn: $toggleStates.twoIsOn)
        DisclosureGroup("Sub-items") {
            Text("Sub-item 1")
        }
    }
}

Пример из https://developer.apple.com/documentation/swiftui/disclosuregroup

Это хорошо, но не позволяет выбирать. Вместо этого поддерево под каждым элементом первого уровня составляется как одна ячейка, что делает невозможным индивидуальный выбор.

Другие вопросы по тегам