Eine zweispaltige Anwendung mit dem NavigationSplitView


Der mit iOS 16 und macOS 13 eingeführte NavigationSplitView wird es einfach, eine Anwendung zu entwicklen, die dem Apple typischen Layout folgen. Auf der linken Seite des Fensters eine Seitenleiste und daneben der Arbeitsbereich. Programme wie Notizen, Erinnerungen und sogar Music und TV folgenden diesem Layout. Je nach Anwendung verändert sich der Inhalt des Arbeitsbereiches, in Abhängigkeit der Auswahl in der Seitenleiste. Dieses Tutorial soll zeigen, wie eine solche App in SwiftUI aussehen kann. Der View im Arbeitsbereich wird bei jeder Auswahl der Seitenleiste komplett ausgetauscht.

Stacks Image 303

Benötigt werden für den Anfang zwei Typen. Ein Enum, um einen Eintrag in der Seitenleiste eindeutig zu identifizieren und eine Struktur, die Informationen zur Repräsentation des Eintrages in der Seitenleiste aufnimmt. In diesem Beispiel soll nicht nur ein Text (displayText) sondern zusätzlich ein Symbol (imageName) für einen Eintrag in der Seitenleiste angezeigt werden.

public enum SideBarItemIdentifier {
    case users
    case trash
    case folders
}

public struct SideBarItem : Identifiable, Hashable {
    public var id : UUID = UUID()
    public var displayText : String
    public var imageName : String
    public var identifier : SideBarItemIdentifier
}  

Im übergeordneten View, also das Fenster, das die zweispaltige Ansicht erhalten soll, werden dann als erstes die Objekte für die Elemente der Seitenleiste erzeugt. Jedes Element erhält einen Text, den Namen eines Bildes und seinen Identifier.

var sideBarItems : [SideBarItem]
init() {
    sideBarItems =  [
        SideBarItem(displayText: "Folders", imageName: "folder.fill", identifier: .folders)
        ,SideBarItem(displayText: "Users", imageName: "person.fill", identifier: .users)
        ,SideBarItem(displayText: "Trash", imageName: "trash.fill", identifier: .trash) ]
} 

Eine Eigenschaft mit dem Namen sideBarVisibility vom Typ NavigationSplitViewVisibility legt das Layout fest, mit dem die Anwendung starten soll. Bei doubleColumn sind Seitenleiste und Arbeitsbereich von Anfang an sichtbar. Der Wert detailOnly würde die Seitenleiste verbergen. Durch eine Schaltfläche, die vom NavigationSplitView automatisch eingefügt wird, kann die Seitenleiste jederzeit aus- und eingeblendet werden.

Eine weitere wichtige Eigenschaft trägt den Namen selectedIdentifier. Sie ist vom Typ des zuvor definierten Enums und wird die Information aufnehmen, welcher View im Arbeitsbereich angezeigt werden soll. Der Wert, der dieser Eigenschaft bei der Initialisierung zugewiesen wird, bestimmt, mit welcher View im Arbeitsbereich die Anwendung startet.

@State var sideBarVisibility : NavigationSplitViewVisibility = .doubleColumn
@State var selectedIdentifier : SideBarItemIdentifier = .users

Der body der View besteht auf oberster Ebene aus einem NavigationSplitView, in dem eine List eingebettet ist. Die List durchläuft sämtliche Elemente für die Seitenleiste und erzeugt für jeden Eintrag einen NavigationLink. Diese wiederum enthält das Design, wie die Einträge in der Seitenleiste angezeigt werden sollen. Im Beispiel ist das lediglich ein HStack mit einem Image und einem Text. Die zweite Eigenschaft des NavigationLink ist sein value. Dort kommt erneut der Identifier aus dem Enum zum Einsatz. Der zweite Teil des NavigationSplitView ist der detail-Bereich, in dem die View für den Arbeitsbereich bestimmt wird. In Abhängigkeit des selectedIdentifier und mit Hilfe einer switch-Anweisung werden verschiedene View bereitgestellt. Im Beispiel sind das Texte. In einer umfangreichen Anwendung wären es umfangreiche Ansichten, die in eigenen Strukturen und separaten Dateien definiert ist.

 var body: some View {
     NavigationSplitView(columnVisibility: $sideBarVisibility) {
         List(sideBarItems, selection: $selectedIdentifier) { item in
             NavigationLink (value:  item.identifier)
             {
                 HStack {
                     Image(systemName: item.imageName)
                     Text(item.displayText)
                 }
             }
         }
     } detail: {
         switch selectedIdentifier {
         case .users:
             Text("This is the Users View")
         case .folders:
             Text("This is the Folders View")
         case .trash:
             Text("This is the Trash View")
         }
     }
 }

Und hier der komplette Code im Zusammenhang:

//  NavigationManagerView.swift
//  NavigationSplitViewDemo
//  Created by Holger Hinzberg 

import SwiftUI

public enum SideBarItemIdentifier {
    case users
    case trash
    case folders
}

public struct SideBarItem : Identifiable, Hashable {
    public var id : UUID = UUID()
    public var displayText : String
    public var imageName : String
    public var identifier : SideBarItemIdentifier
}

struct NavigationManagerView: View {
    
    var sideBarItems : [SideBarItem]
    init()
    {
        sideBarItems =  [
            SideBarItem(displayText: "Folders", imageName: "folder.fill", identifier: .folders)
            ,SideBarItem(displayText: "Users", imageName: "person.fill", identifier: .users)
            ,SideBarItem(displayText: "Trash", imageName: "trash.fill", identifier: .trash)
        ]
    }
    
    @State var sideBarVisibility : NavigationSplitViewVisibility = .doubleColumn
    @State var selectedIdentifier : SideBarItemIdentifier = .users
    
    var body: some View {
        NavigationSplitView(columnVisibility: $sideBarVisibility) {
            List(sideBarItems, selection: $selectedIdentifier) { item in
                NavigationLink (value:  item.identifier)
                {
                    HStack {
                        Image(systemName: item.imageName)
                        Text(item.displayText)
                    }
                }
            }
        } detail: {
            switch selectedIdentifier {
            case .users:
                Text("This is the Users View")
            case .folders:
                Text("This is the Folders View")
            case .trash:
                Text("This is the Trash View")
            }
        }
    }
}

struct NavigationManagerView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationManagerView()
    }
}

Geschrieben am: 15.11.2022
Technologien: Swift, NavigationSplitView