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.

<span class="kwd">public</span><span class="pln"> </span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">SideBarItemIdentifier</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">case</span><span class="pln"> users
    </span><span class="kwd">case</span><span class="pln"> trash
    </span><span class="kwd">case</span><span class="pln"> folders
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">SideBarItem</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Identifiable</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Hashable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">:</span><span class="pln"> UUID </span><span class="pun">=</span><span class="pln"> UUID</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> displayText </span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> imageName </span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> identifier </span><span class="pun">:</span><span class="pln"> </span><span class="typ">SideBarItemIdentifier</span><span class="pln">
</span><span class="pun">}</span><span class="pln">  </span>

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.

<span class="kwd">var</span><span class="pln"> sideBarItems </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">SideBarItem</span><span class="pun">]</span><span class="pln">
init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    sideBarItems </span><span class="pun">=</span><span class="pln">  </span><span class="pun">[</span><span class="pln">
        </span><span class="typ">SideBarItem</span><span class="pun">(</span><span class="pln">displayText</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Folders"</span><span class="pun">,</span><span class="pln"> imageName</span><span class="pun">:</span><span class="pln"> </span><span class="str">"folder.fill"</span><span class="pun">,</span><span class="pln"> identifier</span><span class="pun">:</span><span class="pln"> </span><span class="pun">.</span><span class="pln">folders</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">,</span><span class="typ">SideBarItem</span><span class="pun">(</span><span class="pln">displayText</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Users"</span><span class="pun">,</span><span class="pln"> imageName</span><span class="pun">:</span><span class="pln"> </span><span class="str">"person.fill"</span><span class="pun">,</span><span class="pln"> identifier</span><span class="pun">:</span><span class="pln"> </span><span class="pun">.</span><span class="pln">users</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">,</span><span class="typ">SideBarItem</span><span class="pun">(</span><span class="pln">displayText</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Trash"</span><span class="pun">,</span><span class="pln"> imageName</span><span class="pun">:</span><span class="pln"> </span><span class="str">"trash.fill"</span><span class="pun">,</span><span class="pln"> identifier</span><span class="pun">:</span><span class="pln"> </span><span class="pun">.</span><span class="pln">trash</span><span class="pun">)</span><span class="pln"> </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span>

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.

<span class="lit">@State</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> sideBarVisibility </span><span class="pun">:</span><span class="pln"> </span><span class="typ">NavigationSplitViewVisibility</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">.</span><span class="pln">doubleColumn
</span><span class="lit">@State</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> selectedIdentifier </span><span class="pun">:</span><span class="pln"> </span><span class="typ">SideBarItemIdentifier</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">.</span><span class="pln">users</span>

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.

<span class="pln"> </span><span class="kwd">var</span><span class="pln"> body</span><span class="pun">:</span><span class="pln"> some </span><span class="typ">View</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="typ">NavigationSplitView</span><span class="pun">(</span><span class="pln">columnVisibility</span><span class="pun">:</span><span class="pln"> $sideBarVisibility</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="typ">List</span><span class="pun">(</span><span class="pln">sideBarItems</span><span class="pun">,</span><span class="pln"> selection</span><span class="pun">:</span><span class="pln"> $selectedIdentifier</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> item </span><span class="kwd">in</span><span class="pln">
             </span><span class="typ">NavigationLink</span><span class="pln"> </span><span class="pun">(</span><span class="pln">value</span><span class="pun">:</span><span class="pln">  item</span><span class="pun">.</span><span class="pln">identifier</span><span class="pun">)</span><span class="pln">
             </span><span class="pun">{</span><span class="pln">
                 </span><span class="typ">HStack</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                     </span><span class="typ">Image</span><span class="pun">(</span><span class="pln">systemName</span><span class="pun">:</span><span class="pln"> item</span><span class="pun">.</span><span class="pln">imageName</span><span class="pun">)</span><span class="pln">
                     </span><span class="typ">Text</span><span class="pun">(</span><span class="pln">item</span><span class="pun">.</span><span class="pln">displayText</span><span class="pun">)</span><span class="pln">
                 </span><span class="pun">}</span><span class="pln">
             </span><span class="pun">}</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
     </span><span class="pun">}</span><span class="pln"> detail</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">switch</span><span class="pln"> selectedIdentifier </span><span class="pun">{</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="pun">.</span><span class="pln">users</span><span class="pun">:</span><span class="pln">
             </span><span class="typ">Text</span><span class="pun">(</span><span class="str">"This is the Users View"</span><span class="pun">)</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="pun">.</span><span class="pln">folders</span><span class="pun">:</span><span class="pln">
             </span><span class="typ">Text</span><span class="pun">(</span><span class="str">"This is the Folders View"</span><span class="pun">)</span><span class="pln">
         </span><span class="kwd">case</span><span class="pln"> </span><span class="pun">.</span><span class="pln">trash</span><span class="pun">:</span><span class="pln">
             </span><span class="typ">Text</span><span class="pun">(</span><span class="str">"This is the Trash View"</span><span class="pun">)</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
     </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">}</span>

Und hier der komplette Code im Zusammenhang:

<span class="com">//  NavigationManagerView.swift</span><span class="pln">
</span><span class="com">//  NavigationSplitViewDemo</span><span class="pln">
</span><span class="com">//  Created by Holger Hinzberg </span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">SwiftUI</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">enum</span><span class="pln"> </span><span class="typ">SideBarItemIdentifier</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">case</span><span class="pln"> users
    </span><span class="kwd">case</span><span class="pln"> trash
    </span><span class="kwd">case</span><span class="pln"> folders
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">SideBarItem</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">Identifiable</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Hashable</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">:</span><span class="pln"> UUID </span><span class="pun">=</span><span class="pln"> UUID</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> displayText </span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> imageName </span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> identifier </span><span class="pun">:</span><span class="pln"> </span><span class="typ">SideBarItemIdentifier</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

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

</span><span class="kwd">struct</span><span class="pln"> </span><span class="typ">NavigationManagerView_Previews</span><span class="pun">:</span><span class="pln"> </span><span class="typ">PreviewProvider</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> previews</span><span class="pun">:</span><span class="pln"> some </span><span class="typ">View</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">NavigationManagerView</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span>

Geschrieben am: 15.11.2022
Technologien: Swift, NavigationSplitView