Lektion 2.18 · Stufe 2 – SwiftUI-Einstieg

Toolbars

ca. 15 Minuten Lesezeit

Lernziel

Nach dieser Lektion kannst du Buttons, Menüs und andere Elemente mit .toolbar in die Navigationsleiste und andere Bereiche setzen, die passenden Platzierungen wählen, Einträge gruppieren und die Leiste selbst gestalten.

Das Problem: Wohin mit den Aktionen?

Deine Liste aus der letzten Lektion braucht einen Knopf zum Hinzufügen eines Eintrags. Und vielleicht einen zum Sortieren. Einen zum Bearbeiten. Packst du sie alle in die Liste, verdrängen sie den eigentlichen Inhalt.

iOS hat für solche Aktionen feste Orte: oben rechts in der Navigationsleiste das Plus der Notizen-App, unten die Werkzeugleiste in Safari. Nutzer erwarten Aktionen genau dort. Mit .toolbar platzierst du Elemente an diesen Stellen.

Die erste Toolbar

Swift
struct NotesList: View {
    @State private var notes = ["Einkaufsliste", "Ideen"]

    var body: some View {
        NavigationStack {
            List(notes, id: \.self) { note in
                Text(note)
            }
            .navigationTitle("Notizen")
            .toolbar {
                Button("Neu", systemImage: "plus") {
                    notes.append("Neue Notiz \(notes.count + 1)")
                }
            }
        }
    }
}

Erwartetes Ergebnis

Oben rechts in der Navigationsleiste erscheint ein Plus-Symbol. Jeder Tipp fügt der Liste eine Notiz hinzu.

  • .toolbar { ... } steht wie .navigationTitle an der View im NavigationStack.
  • Ein Button mit systemImage zeigt in der Leiste nur das Symbol. Der Titel „Neu“ wird trotzdem verwendet: VoiceOver liest ihn vor, und bei langem Drücken erscheint er als Hinweis. Lass ihn deshalb nie weg.

Platzierungen mit ToolbarItem

Ohne Angabe wählt SwiftUI selbst, wo ein Element landet, auf dem iPhone meist oben rechts. Mit ToolbarItem bestimmst du die Position:

Swift
.toolbar {
    ToolbarItem(placement: .topBarLeading) {
        EditButton()
    }
    ToolbarItem(placement: .topBarTrailing) {
        Button("Neu", systemImage: "plus") { addNote() }
    }
    ToolbarItem(placement: .bottomBar) {
        Text("\(notes.count) Notizen")
            .font(.footnote)
    }
}
PlatzierungOrt
.topBarLeadingoben links
.topBarTrailingoben rechts
.principaloben in der Mitte, anstelle des Titels
.bottomBarWerkzeugleiste am unteren Rand
.keyboardLeiste über der Tastatur (Lektion 2.12)
.primaryActionwichtigste Aktion, auf dem iPhone oben rechts
.confirmationAction„Fertig“ oder „Speichern“ in Dialogen
.cancellationAction„Abbrechen“ in Dialogen

Bedeutung statt Ort

Die letzten drei Platzierungen beschreiben die Bedeutung statt einer festen Position. SwiftUI setzt sie dorthin, wo sie auf der jeweiligen Plattform hingehören. In Sheets (nächste Lektion) sind .cancellationAction und .confirmationAction die richtige Wahl: „Abbrechen“ landet links, „Speichern“ rechts, wie in allen Systemapps.

Mehrere Elemente gruppieren

Mehrere Elemente an derselben Stelle fasst du mit ToolbarItemGroup zusammen:

Swift
.toolbar {
    ToolbarItemGroup(placement: .bottomBar) {
        Button("Teilen", systemImage: "square.and.arrow.up") { }
        Spacer()
        Button("Favorit", systemImage: "star") { }
        Spacer()
        Button("Löschen", systemImage: "trash") { }
    }
}

Die Spacer verteilen die Buttons gleichmäßig über die Leiste, genau wie in einem HStack (Lektion 2.7).

Für mehrere verwandte Aktionen bietet sich ein Menü an. Es spart Platz und hält die Leiste ruhig:

Swift
.toolbar {
    Menu("Sortieren", systemImage: "arrow.up.arrow.down") {
        Button("Nach Name") { sortByName() }
        Button("Nach Datum") { sortByDate() }
        Divider()
        Button("Zurücksetzen", role: .destructive) { reset() }
    }
}

Ein Tipp auf das Symbol öffnet ein Menü mit den Optionen. Divider trennt Gruppen, und Buttons mit role: .destructive werden rot.

Auch ein Picker funktioniert in einem Menü. Dann bekommt die gewählte Option automatisch ein Häkchen:

Swift
@State private var sortOrder = "Name"

.toolbar {
    Menu("Sortieren", systemImage: "arrow.up.arrow.down") {
        Picker("Sortierung", selection: $sortOrder) {
            Text("Name").tag("Name")
            Text("Datum").tag("Datum")
        }
    }
}

Ein Titel in der Mitte

Mit .principal ersetzt du den Titel durch eine eigene View, zum Beispiel einen segmentierten Picker:

Swift
.toolbar {
    ToolbarItem(placement: .principal) {
        Picker("Ansicht", selection: $view) {
            Text("Liste").tag("Liste")
            Text("Raster").tag("Raster")
        }
        .pickerStyle(.segmented)
        .frame(width: 200)
    }
}

Die Leiste gestalten

Einige Modifier verändern die Navigationsleiste selbst:

Swift
.toolbarBackground(.visible, for: .navigationBar)
.toolbarBackground(.indigo, for: .navigationBar)
.toolbarColorScheme(.dark, for: .navigationBar)
  • .toolbarBackground(.visible, ...) zeigt den Hintergrund der Leiste immer, nicht erst beim Scrollen.
  • .toolbarBackground(.indigo, ...) färbt ihn.
  • .toolbarColorScheme(.dark, ...) sorgt für helle Schrift auf dunklem Grund.

Und ganz ausblenden kannst du die Leiste mit .toolbar(.hidden, for: .navigationBar).

Achtung

Sei mit farbigen Leisten vorsichtig. Das aktuelle iOS-Design setzt auf zurückhaltende, durchscheinende Leisten, die sich dem Inhalt unterordnen. Eine knallbunte Leiste wirkt schnell altmodisch. Mehr zum aktuellen Apple-Design in Lektion 3.30.

Toolbars in Unterseiten

Jede View im Navigationsstapel kann ihre eigene Toolbar haben. Navigiert der Nutzer zur Detailansicht, verschwinden die Einträge der Liste, und die der Detailansicht erscheinen:

Swift
struct NoteDetail: View {
    let note: String
    @State private var isFavorite = false

    var body: some View {
        Text(note)
            .navigationTitle(note)
            .toolbar {
                Button("Favorit", systemImage: isFavorite ? "star.fill" : "star") {
                    isFavorite.toggle()
                }
            }
    }
}

Das Symbol wechselt abhängig vom Zustand. Auch das ist einfach eine Beschreibung (Lektion 2.1).

Typische Fehler

Toolbar erscheint nicht

Die View steht nicht in einem NavigationStack, oder .toolbar hängt am NavigationStack statt an der View darin. Wie beim Titel gilt: Modifier an den Inhalt.

Button ohne Titel

Swift
Button { addNote() } label: {
    Image(systemName: "plus")
}

Funktioniert, aber VoiceOver liest dann nur „plus, Taste“ vor, oder gar nichts Sinnvolles. Nimm Button("Neu", systemImage: "plus") oder ein Label. Die Leiste zeigt trotzdem nur das Symbol.

Zu viele Elemente

Mehr als zwei, drei Elemente pro Seite der Navigationsleiste werden eng, auf kleinen iPhones schneidet iOS sie ab. Fass Nebenaktionen in einem Menu zusammen.

bottomBar erscheint nicht

Die untere Leiste braucht ebenfalls einen NavigationStack. Bei Apps mit TabView (Lektion 3.21) konkurriert sie außerdem mit der Tab-Leiste.

FAQ

Anfänger

Was bedeutet das überhaupt?

Was ist der Unterschied zwischen .toolbar und einem Button im Inhalt? Ein Button in der Toolbar ist immer sichtbar, auch beim Scrollen, und steht dort, wo Nutzer Aktionen erwarten. Ein Button im Inhalt scrollt mit. Für Aktionen, die den ganzen Bildschirm betreffen, wie „Neu“ oder „Bearbeiten“, ist die Toolbar der richtige Ort.

Kann ich eigene Bilder in der Toolbar verwenden? Ja, im label eines Buttons darf jedes Bild stehen. SF Symbols passen sich aber automatisch der Leiste an, deshalb sind sie meist die bessere Wahl.

Fortgeschritten

Warum verhält es sich so?

Was ist das Titelmenü? Mit .toolbarTitleMenu { ... } bekommt der Titel selbst einen kleinen Pfeil und öffnet beim Antippen ein Menü, etwa mit „Umbenennen“ oder „Duplizieren“. Das kennst du aus der Dateien-App.

Kann ich die Toolbar aus einer Unter-View befüllen? Ja. Jede View im Stapel darf .toolbar verwenden, auch tief verschachtelte. SwiftUI sammelt alle Einträge der aktuell sichtbaren Views ein und setzt sie zusammen.

Profi

Was passiert unter der Haube?

Wie kommen die Einträge von der View in die Leiste? Toolbar-Inhalte sind sogenannte Preferences: Informationen, die von Kind-Views nach oben gereicht werden, im Gegensatz zur Umgebung, die nach unten geht. Der NavigationStack liest die Preferences der obersten View aus und baut daraus die Leiste. Mit eigenen PreferenceKeys kannst du dasselbe Prinzip für eigene Zwecke nutzen.

Warum gibt es ToolbarItem und ToolbarContent? .toolbar erwartet einen eigenen Ergebnistyp, ToolbarContent, nicht beliebige Views. Einzelne Views werden automatisch in ToolbarItems verpackt. Willst du Toolbar-Inhalte in eine eigene Struct auslagern, lässt du sie das Protokoll ToolbarContent erfüllen, ganz ähnlich wie View.

Übungen

Anfänger

Übung 1: Notizen mit Toolbar

Baue eine Notizen-Liste mit einem „Neu“-Button oben rechts und einem EditButton oben links. Die Notizen sollen sich löschen lassen.

Lösung Übung 1Erst selbst probieren!
Swift
struct Note: Identifiable {
    let id = UUID()
    var title: String
}

struct NotesView: View {
    @State private var notes = [Note(title: "Einkaufsliste")]

    var body: some View {
        NavigationStack {
            List {
                ForEach(notes) { note in
                    Text(note.title)
                }
                .onDelete { notes.remove(atOffsets: $0) }
            }
            .navigationTitle("Notizen")
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    EditButton()
                }
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Neu", systemImage: "plus") {
                        notes.append(Note(title: "Notiz \(notes.count + 1)"))
                    }
                }
            }
        }
    }
}
Fortgeschritten

Übung 2: Sortiermenü

Erweitere Übung 1: Jede Notiz bekommt ein Erstellungsdatum. Ein Sortiermenü in der Toolbar erlaubt, nach Titel oder nach Datum zu sortieren. Die Auswahl wird mit einem Häkchen angezeigt. Verwende ein Enum für die Sortierung und eine berechnete Eigenschaft sortedNotes.

Profi

Übung 3: Werkzeugleiste unten

Baue eine Detailansicht für eine Notiz mit einer unteren Werkzeugleiste: links ein Favoriten-Button, dessen Symbol sich ändert, in der Mitte ein Text mit der Anzahl der Zeichen und rechts ein Teilen-Button. Für den echten Teilen-Dialog gibt es übrigens ShareLink(item: note.title). Probier ihn aus.

Zusammenfassung

  • .toolbar { ... } setzt Elemente in die Navigationsleiste. Es steht an der View im NavigationStack.
  • ToolbarItem(placement:) bestimmt die Position: .topBarLeading, .topBarTrailing, .principal, .bottomBar, .keyboard.
  • Bedeutungsbasierte Platzierungen wie .confirmationAction und .cancellationAction für Dialoge.
  • ToolbarItemGroup fasst mehrere Elemente zusammen, Menu spart Platz.
  • Buttons in der Toolbar immer mit Titel, auch wenn nur das Symbol sichtbar ist.
  • Jede View im Stapel hat ihre eigene Toolbar.

Spickzettel

Swift
.toolbar {
    ToolbarItem(placement: .topBarLeading) { EditButton() }
    ToolbarItem(placement: .topBarTrailing) {
        Button("Neu", systemImage: "plus") { add() }
    }
    ToolbarItemGroup(placement: .bottomBar) {
        Button("Teilen", systemImage: "square.and.arrow.up") { }
        Spacer()
        Button("Löschen", systemImage: "trash") { }
    }
}

Menu("Mehr", systemImage: "ellipsis.circle") {
    Button("Umbenennen") { }
    Button("Löschen", role: .destructive) { }
}

Lektion geschafft?

Markiere sie als erledigt, damit du deinen Fortschritt siehst.