This is the second post of my little series about a fine-tuned version of my OutlinePicker
. Today, I’d like to propose an improved version of the already presented NodeOutlineGroup
. As stated here the NodeOutlineGroup
gives you control whether the disclosure group should be collapsed or expanded on show up of the outline group. I want to use that for my hierarchical picker. But on start of my picker the pre-selected node might be deep in the tree instead of being one of the root nodes.
So, if I set up the picker with all nodes collapsed the pre-selected node would not be visible immediately and the user might search for it for quite a long time. On the other hand, if I expand all nodes it might be quite a big tree to display on startup. The ideal solution would be that all nodes are collapsed except to those ones which have the selected node as a child or grand child.
To accomplish that the NodeOutlineGroup
needs to get an array of those nodes which should be expanded on startup. This is my proposal for the improved version of NodeOutlineGroup
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// from: https://stackoverflow.com/questions/62832809/list-or-outlinegroup-expanded-by-default-in-swiftui struct NodeOutlineGroup<Node, Content>: View where Node: Hashable, Node: Identifiable, Content: View { let node: Node // the root node to display let childKeyPath: KeyPath<Node, [Node]?> // and the key path to its children @State var isExpanded: Bool = true // do we want to expand everything? let expandedNodes: [Node]? // the array of expanded nodes let content: (Node) -> Content // and what to display on each node var body: some View { if let child = node[keyPath: childKeyPath], // we do have an array for children !child.isEmpty { // and its not empty DisclosureGroup( // show the node as a disclosure group isExpanded: $isExpanded, content: { if isExpanded { // the disclosure group is expanded ForEach(child) { childNode in // show the outline group for each child NodeOutlineGroup(node: childNode, childKeyPath: childKeyPath, isExpanded: isExpanded, expandedNodes: expandedNodes, content: content) } } }, label: { content(node) }) // the line to display for every node .onAppear() { // on startup if let expandedNodes = expandedNodes { // do we have a list of nodes to expand? isExpanded = expandedNodes.contains(node) // is the current node in there? } // set the expansion flag } } else { content(node) // the node has no children, just show the line } } } |
Line 7 declares the new optional input parameter of the list of node to expand. And lines 27-31 check if the current node should be expanded and override the default behaviour of expansion. That’s it. Now, we only have to generate this array of nodes to expand but we’ll keep that for the next post.