SwiftUI Cheatsheet
Im Juni 2025 begann ich mit dem Kurs 100 Days of SwiftUI. Es ist ein großartiger Kurs, und ich bin wirklich beeindruckt, wie viel qualitativ hochwertiger Inhalt und Kurse Paul Hudson bereitstellt – und pflegt!! Paul, vielen Dank dafür! 🙏🏼
Aber es ist eine Menge Inhalt, daher hier meine Notizen – hoffentlich in einem leicht navigierbaren Spickzettel-Format. Ich habe eine grobe Struktur im Kopf, werde den Inhalt jedoch nur dann ausfüllen, wenn ich ihn benötige. Erwarten Sie also keine vollständige Übersicht!
Swift
Für einen umfassenden Überblick siehe Lernen Sie essentielles Swift in einer Stunde.
Im folgenden Kapitel habe ich nur die Teile hinzugefügt, die ich mindestens einmal überprüfen musste.
struct
& berechnete Eigenschaften
struct Employee {
let name: String
var vacationAllocated = 14
var vacationTaken = 0
var vacationRemaining: Int {
vacationAllocated - vacationTaken
}
}
Optionals
- Optionals ermöglichen es uns, das Fehlen von Daten darzustellen, was bedeutet, dass wir sagen können: „Dieser Integer hat keinen Wert“ – das ist anders als eine feste Zahl wie 0.
- Beispiel:
var str:String?
kann einen String oder nil enthalten - Folglich hat alles, was nicht optional ist, definitiv einen Wert, selbst wenn es nur ein leerer String ist.
- Das Entpacken eines Optionals ist der Prozess, in eine Box zu schauen, um zu sehen, was sie enthält: Wenn ein Wert darin ist, wird er zur Verwendung zurückgegeben, andernfalls wird nil enthalten sein.
- Wir können
if let
verwenden, um Code auszuführen, wenn das Optional einen Wert hat, oder guard let, um Code auszuführen, wenn das Optional keinen Wert hat – aber mit guard müssen wir danach immer die Funktion verlassen.
func printSquare(of number: Int?) {
guard let number = number else {
print("Eingabe fehlt")
return
}
print("\(number) x \(number) ist \(number * number)")
}
- Der nil-Koaleszenz-Operator, ??, entpackt und gibt den Wert eines Optionals zurück oder verwendet stattdessen einen Standardwert.
let new = captains["Serenity"] ?? "N/A"
- Optional Chaining ermöglicht es uns, ein Optional innerhalb eines anderen Optionals mit einer praktischen Syntax zu lesen.
- Wenn eine Funktion Fehler werfen könnte, können Sie sie mit try? in ein Optional umwandeln – Sie erhalten entweder den Rückgabewert der Funktion oder nil, wenn ein Fehler auftritt.
Protokolle und Erweiterungen
protocol Vehicle {
func estimateTime(for distance: Int) -> Int
func travel(distance: Int)
}
extension String {
func trimmed() -> String {
self.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
Arrays & Sortierung
Alle Arrays haben eingebaute sort()
und sorted()
Methoden, die verwendet werden können, um das Array zu sortieren.
sort()
sortiert das Array direktsorted()
gibt ein neues, sortiertes Array zurück.
Wenn das Array einfach ist, können Sie einfach sort()
direkt aufrufen, um ein Array vor Ort zu sortieren:
var names = ["Jemima", "Peter", "David", "Kelly", "Isabella"]
names.sort()
Wenn Sie komplexere Strukturen haben, müssen Sie den Vergleich angeben:
struct User {
var firstName: String
}
var users = [
User(firstName: "Jemima"),
User(firstName: "Peter"),
User(firstName: "David"),
User(firstName: "Kelly"),
User(firstName: "Isabella")
]
users.sort {
$0.firstName < $1.firstName
}
Wir können unsere eigenen Typen Comparable
konform machen, und wenn wir das tun, erhalten wir auch eine sorted()
Methode ohne Parameter. Dies erfordert zwei Schritte:
- Fügen Sie die
Comparable
-Konformität zur Definition von User hinzu. - Fügen Sie eine Methode namens
<
hinzu, die zwei Benutzer nimmt und true zurückgibt, wenn der erste vor dem zweiten sortiert werden soll.
So sieht das im Code aus:
struct User: Identifiable, Comparable {
let id = UUID()
var firstName: String
var lastName: String
static func <(lhs: User, rhs: User) -> Bool {
lhs.lastName < rhs.lastName
}
}
Strings
- Sie sind besonders, und es gibt viel zu wissen...
- Das funktioniert nicht:
let name = "Paul"
let firstLetter = name[0]
Daten
Date
, DateComponents
und DateFormatter
enum
enum
werden so erstellt:
enum Weekday {
case monday, tuesday, wednesday, thursday, friday
}
Eine switch
-Anweisung mit enum
sieht so aus:
switch loadingState {
case .loading:
LoadingView()
case .success:
SuccessView()
case .failed:
FailedView()
}
SwiftUI
- Interactful ist ein schönes Tool, um die verschiedenen Ansichten und Komponenten zu navigieren und auszuprobieren.
- Human Interfaces Guideline
Ansichten
- Alles ist eine Ansicht in SwiftUI 😜
- Code ausführen, wenn eine Ansicht angezeigt wird, mit
onAppear()
.
Sogar ForEach
ist eine Ansicht, deshalb können wir schreiben
ForEach(0..<5) {
Text("Zeile \($0)")
}
Hinweis: Wir können nicht ForEach(0..<5)
schreiben, weil ForEach
einen Range<Int>
erwartet, nicht einen ClosedRange<Int>
!
ForEach
Ansicht
ForEach
ist eine Ansicht, die aus den Unteransichten besteht, die in jeder Schleifeninstanz erstellt werden.
Wir verwenden es typischerweise, um Unteransichten basierend auf einem Zähler oder einem Array zu erstellen.
ForEach
mit einem Array:
import SwiftUI
struct ContentView: View {
let items = ["Apfel", "Banane", "Kirsche"]
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
}
}
}
Dateneingabe
TextField
Picker
Picker
wird verwendet, um eine von vielen möglichen Auswahlen zu treffen.
Ein regulärer Picker sieht so aus:
Picker("Anzahl der Personen", selection: $numberOfPeople) {
ForEach(2 ..< 100 , id: \.self) {
Text("\($0) Personen")
}
}
Ein Picker
kann mit dem PickerStyle
-Modifier modifiziert werden:
Picker("Trinkgeldprozentsatz", selection: $tipPercentage) {
ForEach(tipPercentages, id: \.self) {
Text($0, format: .percent)
}
}
.pickerStyle(.segmented)
Stepper
Ein Stepper ist ein zweigeteiltes Steuerelement, das Menschen verwenden, um einen inkrementellen Wert zu erhöhen oder zu verringern.
@State private var count: Int = 0
var body: some View {
Stepper("\(count)",
value: $count,
in: 0...100
)
}
DatePicker
für Daten. Verwenden Sie den ParameterdisplayedComponents
, um Daten oder Zeiten zu steuern.Form
Picker
- Navigationsleiste
Warnungen & Bestätigungsdialoge
Warnungen
Warnungen sind eine Erweiterung einer Ansicht mit einer Booleschen Variablen, die entscheidet, ob sie angezeigt werden oder nicht.
struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Warnung anzeigen") {
showingAlert = true
}
.alert("Wichtige Nachricht", isPresented: $showingAlert) {
Button("OK") { }
}
}
}
Bestätigungsdialoge
Verwenden Sie sie, wenn viele Schaltflächen/Optionen verfügbar sind. Für Beispielcode siehe dieses Repo.
Hinweis: Vor iOS 26 schoben sie sich von unten ins Bild, in iOS 26 erscheinen sie innerhalb des Bildschirms.
Bestätigungsdialog in iOS 18.5:
Bestätigungsdialog in iOS 26:
Text
Text
ist ein Textfeld, das Text beschreibt.
Hinweis: Textfelder mit unterschiedlichem Styling können zusammengefügt werden, um ein großes Textfeld mit Teilen mit unterschiedlichem Styling zu bilden:
Text(page.title)
.font(.headline)
+ Text(": ") +
Text("Seitenbeschreibung hier")
.italic()
Und Sie erhalten einen Text mit unterschiedlichen Stilen kombiniert:
Listen
Scrollende Datenlisten mit List
erstellen, insbesondere wie sie Zeilen direkt aus Datenarrays erstellen kann.
List {
Section {
Label("Sonne", systemImage: "sun.max")
Label("Wolke", systemImage: "cloud")
Label("Regen", systemImage: "cloud.rain")
}
}
Schaltflächen in Listen: Wenn Sie eine Schaltfläche in eine Liste einfügen, wird das GESAMTE Listenelement anklickbar! Wenn es mehr als eine Schaltfläche in einer Liste gibt, klicken Sie, wo immer Sie auf das Listenelement klicken, ALLE Schaltflächen nacheinander!
Um das zu beheben und das gewünschte Verhalten zu erhalten, verwenden Sie .buttonStyle(.plain)
Dasselbe gilt für HStack:
HStack {
if label.isEmpty == false {
Text(label)
}
ForEach(1..<maximumRating + 1, id: \.self) { number in
Button {
rating = number
} label: {
image(for: number)
.foregroundStyle(number > rating ? offColor : onColor)
}
}
}
.buttonStyle(.plain)
Bilder
struct ContentView: View
{
var body: some View {
Image (example)
.resizable ()
.scaledToFit ()
.frame(width: 300, height: 300)}
}
Ersetzen Sie ScaledToFit
durch ScaledToFill
und erhalten Sie
struct ContentView: View {
var body: some View {
Image (•example)
.resizable ()
.scaledToFit()
.containerRelativeFrame(horizontal) { size, axis in
size * 0.8
}
}
}
Werkzeugleiste
Bundle
Dateien aus unserem App-Bundle lesen, indem wir ihren Pfad mit der Bundle
-Klasse nachschlagen, einschließlich des Ladens von Strings von dort.
Animationen
Behandelt in Tag 32-34. TODO Ich muss die Clips noch einmal ansehen, um meine Notizen/Spickzettel zu extrahieren.
- Animationen implizit mit dem
animation()
-Modifier erstellen. - Animationen mit Verzögerungen und Wiederholungen anpassen und zwischen Ease-in-Ease-out- und Spring-Animationen wählen.
- Den
animation()
-Modifier an Bindungen anhängen, damit wir Änderungen direkt von UI-Steuerelementen aus animieren können. withAnimation()
verwenden, um explizite Animationen zu erstellen.- Mehrere
animation()
-Modifier an eine einzelne Ansicht anhängen, damit wir den Animationsstapel steuern können.
Daten laden
Wenn es synchron ist:
View...
.onAppear(loadIt)
?? Wie wird es gemacht, wenn loadIt
asynchron ist??
Werte an Ansichten übergeben und zurückgeben
Wie in Auswählen und Bearbeiten von Kartenanmerkungen gesehen
Stellen Sie sich vor, ich habe eine Ansicht, die als Sheet geöffnet wird und ein Location
(ein selbst definiertes struct
) erhält:
struct EditView: View {
@Environment(\.dismiss) var dismiss
var location: Location
@State private var name: String
@State private var description: String
var body: some View {
NavigationStack {
Form {
Section {
TextField("Ortsname", text: $name)
TextField("Beschreibung", text: $description)
}
}
.navigationTitle("Ortsdetails")
.toolbar {
Button("Speichern") {
dismiss()
}
}
}
}
}
Um die Location
zu übergeben, mache ich einen zusätzlichen Initialisierer:
init(location: Location) {
self.location = location
_name = State(initialValue: location.name)
_description = State(initialValue: location.description)
}
Um Daten an den aufrufenden Code zurückzugeben, übergebe ich eine Methode onSave
. Zuerst erstelle ich eine zusätzliche Variable in meiner View-Struktur:
var onSave: (Location) -> Void
und erweitere dann den Initialisierer wie folgt:
init(location: Location, onSave: @escaping (Location) -> Void) {
self.location = location
self.onSave = onSave
_name = State(initialValue: location.name)
_description = State(initialValue: location.description)
}
Dieser @escaping
-Teil ist wichtig und bedeutet, dass die Funktion für die spätere Verwendung aufbewahrt wird, anstatt sofort aufgerufen zu werden, und es ist hier notwendig, weil die onSave
-Funktion nur aufgerufen wird, wenn der Benutzer auf Speichern drückt.
Sheet
s & NavigationStack
s
Um eine Ansicht als Sheet zu öffnen:
struct ContentView: View {
@State private var showingAddExpense = false
var body: some View {
NavigationStack {
VStack {
// Some code here
}
.navigationTitle("iExpense")
.toolbar {
Button("Ausgabe hinzufügen", systemImage: "plus") {
showingAddExpense = true
}
}
.sheet(isPresented: $showingAddExpense) {
AddView(expenses: expenses)
}
}
}
}
Netzwerk
So senden Sie etwas an einen HTTPS-Endpunkt:
func placeOrder() async {
guard let encoded = try? JSONEncoder().encode(order) else {
print("Bestellung konnte nicht codiert werden")
return
}
let url = URL(string: "https://reqres.in/api/cupcakes")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
do {
let (data, other) = try await URLSession.shared.upload(for: request, from: encoded)
let decodedOrder = try JSONDecoder().decode(Order.self, from: data)
confirmationMessage = "Ihre Bestellung über \(decodedOrder.quantity)x \(Order.types[decodedOrder.type].lowercased()) Cupcakes ist auf dem Weg!"
showingConfirmation = true
} catch {
print("Checkout fehlgeschlagen: \(error.localizedDescription)")
}
}
Daten speichern
Ich kenne 3 Möglichkeiten, Daten in Swift/UI zu speichern:
- UserDefaults: Am besten geeignet, um kleine Datenmengen zu speichern. Zum Beispiel App-Einstellungen.
- TODO Schreiben in das Dokumentenverzeichnis
- SwiftData
UserDefaults
Wir benötigen ein paar Dinge:
- Unsere Daten müssen
Codable
sein, damit wir späterJSONEncoder
erstellen können - UserDefaults zum Speichern und Laden unserer Daten
- Ein benutzerdefinierter Initialisierer für die Datenklasse, damit sie automatisch geladen wird
- Ein
didSet
für die Daten, damit sie immer automatisch gespeichert werden, wenn Daten hinzugefügt oder geändert werden.
Die Daten Codable
zu machen, ist meistens nicht allzu schwer: Solange die Komponenten Codable
sind, ist es auch die gesamte Klasse.
So kann das Speichern der Daten in einem didSet
aussehen:
var items = [ExpenseItem]() {
didSet {
if let encoded = try? JSONEncoder().encode(items) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
Und so könnte ein Initialisierer aussehen, der die Daten lädt:
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from: savedItems) {
items = decodedItems
return
}
}
items = []
}
SwiftData
Die beweglichen Teile, die wir haben, sind
- Das
Model
: Dies ist die Datenstruktur. Das/die Objekt(e) und seine/ihre Felder - Der
ModelContainer
: Dies ist der persistente Speicher. Denken Sie daran wie die Datei, in die die Daten auf dem Server geschrieben werden. - Der
ModelContext
: Das ist die im Speicher gehaltene Version Ihrer Daten und wo die Datenänderungen gehalten werden, bevor sie imModelContainer
gespeichert werden.
Um Ihre Software für die Verwendung von SwiftData zu aktivieren, erstellen Sie