Comment afficher des données hiérarchiques avec SwiftUI 4

Qui d'entre vous n'a jamais été confronté au problème d'afficher des structures hiérarchiques au sein d'une application ?
SwiftUI offre cette possibilité avec seulement quelques lignes de code, en utilisant les vues Lists et OutlineGroup . Dans cet article, je vais vous montrer comment procéder en quelques étapes.
Supposons que nous ayons besoin d'afficher des informations hiérarchiques dans une vue Liste, permettant à l'utilisateur de masquer et d'afficher différentes sections de la hiérarchie.
Par exemple, nous voulons naviguer dans la partie suivante d'un système de fichiers, qui comprend des répertoires et des fichiers. Il s'agit d'un arbre hiérarchique typique.

Ensuite, démarrez et lancez Xcode pour créer le nouveau projet d'application nommé HierarchicalDemo :

La première étape est la création d'une structure qui modélise un nœud d'arbre hiérarchique. Dans l'inspecteur de projet, sous le groupe Modèle , nous créons un fichier Swift nommé NodeInfo.swift et définissons la structure NodeInfo :

Chaque nœud a un nom, une image et une couleur qui le représentent selon qu'il s'agit d'un répertoire ou d'un fichier, il peut avoir des nœuds enfants. La structure est conforme au protocole Identifiable , de sorte que chaque instance peut être identifiée de manière unique dans la vue Liste.
struct NodeInfo: Identifiable{
var id = UUID()
var name: String
var image: String
var color: Color
var children: [NodeInfo]?
}
struct NodeCell: View {
var nodeItem: NodeInfo
var body: some View {
HStack{
Image(systemName: nodeItem.image)
.resizable()
.scaledToFit()
.frame(width: 25,height: 25)
.foregroundColor(nodeItem.color)
Text(nodeItem.name)
}
}
}
Nous avons maintenant un modèle pour un nœud de la hiérarchie et une vue pour une ligne dans la vue Liste. L'étape suivante consiste à définir dans la structure ContentView le tableau constant NodeItems qui implémente l'arborescence du système de fichiers, ci-dessus :
let nodeItems: [NodeInfo] =
[NodeInfo(name: "ROOT", image: "folder",color: .orange, children:
[NodeInfo(name:"Documents", image: "folder.circle",color: .blue, children:[NodeInfo(name: "Accounting", image: "folder.circle",color: .blue, children: [NodeInfo(name: "rental.doc", image: "doc.circle",color: .cyan),NodeInfo(name: "invoice1.doc", image: "doc.circle", color: .cyan),NodeInfo(name: "invoice2.doc", image: "doc.circle",color: .cyan)])]),
NodeInfo(name: "Photo", image: "folder.circle",color: .blue, children: [NodeInfo(name: "Selfie", image: "folder.circle",color:.blue,children: [NodeInfo(name: "john.png", image: "photo.circle",color: .green),NodeInfo(name: "Julie.png", image: "photo.circle",color: .green)]),NodeInfo(name: "mybirthday.png", image: "photo.circle",color: .green)]),
NodeInfo(name: "Video", image: "folder.circle",color:.blue, children: [NodeInfo(name: "Holiday", image: "folder.circle",color:.blue,children: [NodeInfo(name: "mountins.wav", image: "video.circle",color: .orange),NodeInfo(name: "sea.wav", image: "video.circle",color:.orange)]),NodeInfo(name: "birthday.mp3", image: "video.circle.fill",color:.orange),NodeInfo(name: "cat.wav", image: "video.circle",color:.orange)]),
NodeInfo(name: "booking.pdf", image: "doc.circle",color: .green)])]
Comme nous voulons que la liste ait un en-tête personnalisé, dans ContentView.swift , nous définissons une structure SectionHeader :
struct SectionHeader: View{
var nodeItem: NodeInfo
var body: some View{
HStack{
Image(systemName: nodeItem.image)
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
Text(nodeItem.name)
.font(.title2.bold())
}
.foregroundColor(nodeItem.color)
}
}
Il est maintenant temps de construire notre liste hiérarchique. Dans contentView body nous tapons le code ci-dessous :
var body: some View {
List{
ForEach(nodeItems) { item in
Section(header: SectionHeader(nodeItem: item)){
OutlineGroup(item.children ?? [NodeInfo](), children: \.children){child in
NodeCell(nodeItem: child)
}
}
}
}
.listStyle(InsetListStyle())
}

Et voici le résultat final :
