Erstellen Sie interaktive SwiftUI-Vorschauen, indem Sie jeder Vorschau einen eigenen lokalen Zustand geben, ein beobachtbares View-Modell an den Editor übergeben und bearbeitbare Eigenschaften über Bindings verfügbar machen. Diese Anleitung erstellt einen kleinen macOS-Editor für Beschriftungen mit synthetischen Beispieltexten.
Festlegen, wo der Vorschauzustand gespeichert wird
Die Aufgabe besteht darin, eine Desktop-App zur Bearbeitung von Beschriftungen in der Vorschau anzuzeigen. Dabei kann jemand den Titel und den Detailtext einer Beschriftung ändern und anschließend festlegen, ob der Detailtext angezeigt wird. Der Editor soll sofort reagieren, und jede Vorschau soll mit einer ausdrücklich festgelegten Beispielkonfiguration beginnen.
Verwenden Sie die kleinste Eingabe, die die Interaktion unterstützt. Apple empfiehlt, Ansichten nur die Daten zu übergeben, die sie benötigen, da komplexe Abhängigkeiten die Erstellung von Vorschauen erschweren. Eine statische Beschriftung kann einen String entgegennehmen; ein Editor benötigt eine schreibbare Verbindung zu seinem Zustand. Vorschauen zu Ihren Interface-Dateien hinzufügen.
| Was die Ansicht benötigt | Geeignete Eingabe | Wo der Beispielzustand gespeichert wird |
|---|---|---|
| Festen Text anzeigen | Einen unveränderlichen String | Direkt in der Vorschau |
| Einen Wert bearbeiten, der dem übergeordneten Element gehört | Eine @Binding-Eigenschaft |
Lokales @State im Vorschau-Host |
| Mehrere zusammengehörige, beobachtbare Eigenschaften bearbeiten | Ein über @Bindable verfügbares beobachtbares Modell |
Eine Modellinstanz, die im @State des Vorschau-Hosts gespeichert ist |
| Ein über die Umgebung bereitgestelltes Modell lesen | Eine Umgebungsabhängigkeit | Explizite Injektion am Stamm der Vorschau |
In diesem Beispiel enthält das View-Modell zwei Strings, eine Sichtbarkeitseinstellung und einen berechneten Anzeigetitel. Jede Vorschau besitzt eine eigene Instanz. Dadurch sind die Ausgangswerte direkt neben der Vorschau-Deklaration zu sehen, und das Beispiel hängt nicht von einem gemeinsam genutzten veränderlichen Singleton ab.
Ein macOS-SwiftUI-Ziel vorbereiten
Verwenden Sie Xcode 16 oder neuer für die hier gezeigte Inline-Syntax @Previewable. Apple hat dieses Makro in Xcode 16 eingeführt. Legen Sie für dieses Beispiel macOS 14 oder neuer als Bereitstellungsziel fest, da SwiftUI dort Observation unterstützt; Apples aktuelle Referenz zu Previewable() führt ebenfalls macOS 14 als unterstützte Version auf. Xcode 16 – Versionshinweise, Modelldaten in Ihrer App verwalten, Previewable().
Fügen Sie einem vorhandenen macOS-SwiftUI-App-Ziel eine Swift-Datei namens LabelEditor.swift hinzu. Die nächsten beiden Codeblöcke bilden zusammen das vollständige Vorschau-Beispiel. Behalten Sie den vorhandenen Einstiegspunkt der App bei; diese Datei definiert das Modell, den Editor und die Vorschauen.
Der Code verwendet die Schreibweise @State, die mit den unterstützten Toolchains funktioniert. Apples aktuelle Dokumentation unterscheidet die Implementierung: Xcode 26 und früher verwenden den Property-Wrapper State, während Xcode 27 und neuer das Makro State() verwenden. Berücksichtigen Sie diesen Unterschied, wenn Sie generierten Code untersuchen oder die Kosten der Initialisierung beurteilen. State, State().
Das beobachtbare Modell und die bearbeitbare Ansicht erstellen
Wenden Sie @Observable auf das Modell an, damit SwiftUI die von der Oberfläche gelesenen Eigenschaften verfolgen kann. Verwenden Sie im Editor @Bindable, um die Property-Bindings zu erstellen, die seine Textfelder und sein Toggle benötigen. Apple dokumentiert diese Kombination für die Bearbeitung beobachtbarer Modelle. Modelldaten in Ihrer App verwalten, Bindable.
import Foundation
import Observation
import SwiftUI
@MainActor
@Observable
final class LabelViewModel {
var title: String
var detail: String
var showsDetail: Bool
init(
title: String,
detail: String,
showsDetail: Bool = true
) {
self.title = title
self.detail = detail
self.showsDetail = showsDetail
}
var displayedTitle: String {
let trimmed = title.trimmingCharacters(
in: .whitespacesAndNewlines
)
return trimmed.isEmpty ? "Untitled label" : trimmed
}
}
@MainActor
struct LabelEditor: View {
@Bindable var model: LabelViewModel
var body: some View {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .leading, spacing: 6) {
Text("Title")
TextField("Label title", text: $model.title)
}
VStack(alignment: .leading, spacing: 6) {
Text("Detail")
TextField("Label detail", text: $model.detail)
}
Toggle("Show detail", isOn: $model.showsDetail)
Divider()
Text("Rendered label")
.font(.caption)
.foregroundStyle(.secondary)
VStack(alignment: .leading, spacing: 8) {
Text(model.displayedTitle)
.font(.title2)
.fixedSize(horizontal: false, vertical: true)
if model.showsDetail {
Text(model.detail)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(
.quaternary,
in: RoundedRectangle(cornerRadius: 10)
)
}
.textFieldStyle(.roundedBorder)
.padding(20)
}
}
Das Modell übernimmt absichtlich alle Beispielwerte über seinen Initializer. Dieser weist lediglich lokale Eigenschaften zu. Die Ersatzwertberechnung ändert, was die gerenderte Beschriftung anzeigt, ohne den eingegebenen Titel zu überschreiben: Ein Titel, der nur aus Leerraum besteht, bleibt bearbeitbar, während das gerenderte Ergebnis Untitled label lautet.
Der Aufrufer übergibt dem Editor sein Modell. Die Deklaration @Bindable stellt Bindings wie $model.title bereit; der Zustand wird im Vorschau-Host gespeichert. Wenn diese Ansicht lediglich die Modellwerte anzeigte, wäre eine einfache Modellreferenz für die Beobachtung ausreichend. Der Bindable-Speicher ist hier sinnvoll, weil die Steuerelemente schreibbare Property-Bindings benötigen. Bindable.
Jeder Vorschau einen eigenen Beispielzustand geben
Fügen Sie die folgenden Deklarationen derselben Datei hinzu. Jede erstellt ein eigenes Modell mit ausdrücklich festgelegten synthetischen Werten. Die Breiten dienen der beispielhaften Überprüfung des Layouts und sind keine empfohlenen Mindestfenstergrößen.
#Preview("Standard label") {
@Previewable @State var model = LabelViewModel(
title: "Sample tile",
detail: "Blue square, medium size"
)
LabelEditor(model: model)
.frame(width: 420)
.preferredColorScheme(.light)
}
#Preview("Long label in a narrow editor") {
@Previewable @State var model = LabelViewModel(
title: "Sample collection with a longer descriptive label",
detail: "Round tiles arranged in three columns"
)
LabelEditor(model: model)
.frame(width: 300)
.preferredColorScheme(.dark)
}
#Preview("Blank title and hidden detail") {
@Previewable @State var model = LabelViewModel(
title: " ",
detail: "An optional detail line",
showsDetail: false
)
LabelEditor(model: model)
.frame(width: 420)
}
@Previewable macht die lokale Zustandsdeklaration zu einem Bestandteil einer eingebetteten SwiftUI-Ansicht, die von #Preview generiert wird. Apple dokumentiert, dass markierte Deklarationen zu Eigenschaften dieser generierten Ansicht werden. Platzieren Sie die Deklaration am Stamm des Vorschau-Closures; @Previewable außerhalb eines Vorschau-Blocks zu verwenden, führt zu einem Fehler. Previewable().
Die resultierende Besitzkette ist eindeutig: Der generierte Vorschau-Host speichert das Modell in @State, LabelEditor erhält die Modellreferenz und @Bindable stellt dessen bearbeitbare Eigenschaften bereit. SwiftUI unterstützt das Speichern eines Observation-Modells in einem Zustand und die Übergabe dieser Referenz an untergeordnete Ansichten. State.
Betrachten Sie die Initializer-Werte als Ausgangswerte. Eine interaktive Vorschau kann sie während der Ausführung ändern. Wenn Sie ein bestimmtes Layout reproduzieren möchten, wechseln Sie vor dem Vergleich der ursprünglichen Darstellung zu einer neuen Vorschauinstanz.
Das Verhalten anhand einer kleinen Szenariomatrix prüfen
Öffnen Sie LabelEditor.swift, wählen Sie Show Canvas und dann über die Canvas-Tabs eine benannte Vorschau aus. Wenn die Canvas angehalten ist, wählen Sie Restart. Aktivieren Sie den Modus Live, um die Texteingabe und die Steuerelementlogik auszuprobieren. Diese Steuerelemente werden in Apples aktuellem Canvas-Leitfaden beschrieben. Mit Vorschauen in der Canvas interagieren.
Mit den folgenden Prüfungen lassen sich Zustandsprobleme von Layoutproblemen unterscheiden. Die erwarteten Ergebnisse ergeben sich aus dem Beispielcode; es handelt sich nicht um berichtete Testergebnisse.
| Szenario | Aktion | Erwartetes Ergebnis |
|---|---|---|
| Standardbeschriftung | Ersetzen Sie den Titel durch Amber circle |
Der angezeigte Titel ändert sich sofort |
| Sichtbarkeit des Details | Schalten Sie Show detail aus und wieder ein |
Das Detail verschwindet und erscheint mit seinem beibehaltenen Text wieder |
| Leerer Titel | Entfernen Sie alle Zeichen aus dem Titel | Der angezeigte Titel lautet Untitled label |
| Titel mit Leerzeichen | Geben Sie nur Leerzeichen ein | Das Feld behält diese Leerzeichen bei; der angezeigte Titel verwendet den Ersatzwert |
| Schmaler Editor | Untersuchen Sie die Vorschau mit dem langen Titel | Der angezeigte Titel wird innerhalb der Karte umgebrochen |
| Getrennte Ausgangswerte | Bearbeiten Sie eine Vorschau und prüfen Sie dann eine andere, neue Vorschau | Die andere Vorschau beginnt mit ihren jeweils deklarierten Modellwerten |
Wenn die Texteingabe funktioniert, aber die gerenderte Ausgabe unverändert bleibt, prüfen Sie die Verbindung zwischen dem Feld-Binding und der Eigenschaft, aus der die Ausgabe liest. Hier führen beide Wege direkt oder über displayedTitle zu model.title. Wenn das Umschalten der Sichtbarkeit den Detailtext löscht, suchen Sie nach Code, der einen leeren Wert zuweist; dieses Beispiel ändert lediglich showsDetail.
In der Rechercheumgebung standen weder ein Swift-Compiler noch eine macOS-Vorschau-Laufzeitumgebung zur Verfügung. Daher wurden diese Beispiele anhand der dokumentierten APIs geprüft, aber weder kompiliert noch ausgeführt. Erstellen Sie das Ziel und führen Sie die Interaktionsprüfungen in Xcode durch, bevor Sie den Editor anpassen.
Gemeinsame Einrichtung von bearbeitbaren Ausgangswerten trennen
Bei einem so kleinen Modell bleibt jede Vorschau durch eine lokale Erstellung leicht verständlich. Falls mehrere Vorschauen später eine aufwendige, wiederverwendbare Einrichtung benötigen, stellt Apple PreviewModifier bereit: Sein gemeinsam genutzter Kontext wird zwischengespeichert und für teilnehmende Vorschauen wiederverwendet. Die API erfordert macOS 15 oder neuer. PreviewModifier.
Aus diesem dokumentierten Verhalten ergibt sich eine praktische Designentscheidung: Lassen Sie unabhängig bearbeitbare Modell-Ausgangswerte lokal und verwenden Sie einen gemeinsamen Kontext nur für Einrichtungsschritte, die teilnehmende Vorschauen gezielt wiederverwenden sollen. Ein gemeinsam genutztes veränderliches Modell würde auch seine Änderungen teilen, wodurch sich einzelne Szenarien schwerer vergleichen lassen.
Wenn eine vorhandene Ansicht ihr Modell über die Umgebung anhand des Typs abruft, injizieren Sie die erforderliche Instanz am Stamm der Vorschau. Apple dokumentiert, dass ein fehlendes, nicht optionales Modell des angeforderten Typs eine Ausnahme auslöst. Eine Vorschau muss daher die Abhängigkeiten bereitstellen, die ihre ausgewählte Stammansicht tatsächlich liest. Modelldaten in Ihrer App verwalten.
Die Grenzen von AppKit und TextKit wahren
Der obige Editor verwendet SwiftUI-Steuerelemente. Wenn Ihr vorhandener macOS-Editor eine AppKit-Textansicht enthält, verwenden Sie NSViewRepresentable, um diese Ansicht in die SwiftUI-Hierarchie einzubinden. Erstellen und konfigurieren Sie sie über makeNSView und wenden Sie den eingehenden SwiftUI-Zustand anschließend über updateNSView an. Leiten Sie AppKit-Delegate-Änderungen über einen Coordinator weiter, wenn Änderungen zurück zu SwiftUI gelangen müssen. Überlassen Sie SwiftUI die Steuerung des Frames und der Bounds der repräsentierten Ansicht. NSViewRepresentable.
Apple unterstützt auch direkte Vorschauen von AppKit-Ansichten und View-Controllern. @Previewable ist jedoch eine reine SwiftUI-Funktion und gilt nicht für direkte AppKit-Vorschauen. Um das Muster mit lokalem Zustand aus diesem Artikel mit AppKit-Inhalten zu verwenden, zeigen Sie einen SwiftUI-Host mit dem Representable in der Vorschau an. Vorschauen zu Ihren Interface-Dateien hinzufügen.
Behalten Sie bei der Erstellung eines NSTextView die Wahl Ihrer Text-Engine bei. Apple dokumentiert NSTextView(usingTextLayoutManager: true) zur Auswahl von TextKit 2 bei der Initialisierung. Apples TextKit-Leitfaden erklärt außerdem, dass der Zugriff auf layoutManager von TextKit 1 für diese Textansichtsinstanz einen einmaligen Fallback auslösen kann. Verwenden Sie die passenden TextKit-2-APIs, wenn dies die gewünschte Engine ist; eine Vorschau hebt diese Grenzen nicht auf. Neues in TextKit und Textansichten.
Vorschaufehler an der Zuständigkeitsgrenze untersuchen
Beginnen Sie mit der ersten Build-Diagnose. Swift-Makros generieren Code zur Kompilierzeit. Deshalb kann ein Fehler, der an einem Makroaufruf markiert ist, generierte Deklarationen betreffen. Mit dem Xcode-Befehl Editor > Expand Macro können Sie diesen Code untersuchen. Makros anwenden.
Prüfen Sie anschließend den Zustandsmechanismus. Ein vorhandenes, auf Combine basierendes ObservableObject benötigt das dazugehörige Besitzmuster: Apple warnt, dass das Speichern eines solchen Objekts in State Änderungen seiner veröffentlichten Eigenschaften nicht verfolgt, und empfiehlt Eigentümern stattdessen StateObject. Das Beispiel hier verwendet @Observable; sein Zustandsbesitz folgt daher dem Observation-Pfad. State.
Richten Sie die fertige Vorschau-Datei auf explizite Ausgangswerte und sichtbare Interaktionen aus. Sobald die Szenarien mit Standardtitel, langem Titel und leerem Titel wie erwartet funktionieren, können Sie denselben Editor mit einem Modell wiederverwenden, das der passende übergeordnete View Ihrer App besitzt. Die Vorschau-Ausgangswerte bleiben eine kompakte Möglichkeit, diese Oberflächenzustände nachzustellen.
Quellen
- Vorschauen zu Ihren Interface-Dateien hinzufügen — Apple
- Xcode 16 – Versionshinweise — Apple
- Modelldaten in Ihrer App verwalten — Apple
- Previewable() — Apple
- Bindable — Apple
- State — Apple
- State() — Apple
- Mit Vorschauen in der Canvas interagieren — Apple
- PreviewModifier — Apple
- NSViewRepresentable — Apple
- Neues in TextKit und Textansichten — Apple
- Makros anwenden — Apple