WidgetKit iOS 17+: Interaktive Widgets mit App Intents
Vollständige Anleitung zur Erstellung interaktiver iOS-Widgets mit WidgetKit und App Intents. Buttons, Toggles, Animationen und Best Practices für iOS 17+ in 2026.

iOS 17 hat WidgetKit durch die Einführung nativer Interaktivität revolutioniert. Widgets sind keine statischen Anzeigen mehr: Sie können nun direkt vom Home-Bildschirm aus auf Benutzeraktionen reagieren, ohne dass die App geöffnet werden muss. Diese wichtige Weiterentwicklung basiert auf dem App Intents Framework und bietet eine flüssige, moderne Benutzererfahrung.
Dieser Artikel zeigt die vollständige Erstellung interaktiver iOS 17+ Widgets, von der Projektkonfiguration bis zu fortgeschrittenen Mustern mit Animationen und Zustandsverwaltung.
Architektur Interaktiver Widgets
Die Interaktivität der iOS 17+ Widgets funktioniert über das App Intents Framework. Im Gegensatz zu traditionellen Deep Links, die die App öffnen würden, ermöglichen App Intents die Code-Ausführung direkt vom Widget aus und aktualisieren anschließend automatisch die Anzeige mit den neuen Daten.
import WidgetKit
import SwiftUI
import AppIntents
// Die Architektur basiert auf drei Hauptkomponenten:
// 1. Widget Timeline Provider - liefert die Daten
// 2. Widget View - zeigt das Interface mit Button/Toggle
// 3. App Intent - führt die Aktion bei Berührung aus
struct TaskWidget: Widget {
// Eindeutige Widget-Kennung
let kind: String = "TaskWidget"
var body: some WidgetConfiguration {
// StaticConfiguration für Widgets ohne Parameter
StaticConfiguration(
kind: kind,
provider: TaskTimelineProvider()
) { entry in
TaskWidgetView(entry: entry)
// Erforderlich für App Intents
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Aufgaben")
.description("Verwalten Sie Ihre Aufgaben vom Home-Bildschirm aus.")
.supportedFamilies([.systemSmall, .systemMedium])
}
}Das Widget deklariert seine Konfiguration und gibt den Provider an, der die Daten liefert. Das Attribut .containerBackground ist seit iOS 17 für interaktive Widgets verpflichtend.
Erstellung des Timeline Providers
Der Timeline Provider bestimmt, wann und wie das Widget aktualisiert wird. Bei interaktiven Widgets muss er auch auf Änderungen reagieren, die durch App Intents ausgelöst werden.
import WidgetKit
import SwiftUI
// Entry repräsentiert den Widget-Zustand zu einem bestimmten Zeitpunkt
struct TaskEntry: TimelineEntry {
let date: Date
let tasks: [Task]
// Ladezustand für visuelles Feedback
var isLoading: Bool = false
}
// Datenmodell, das zwischen App und Widget geteilt wird
struct Task: Identifiable, Codable {
let id: UUID
var title: String
var isCompleted: Bool
var priority: Priority
enum Priority: String, Codable {
case low, medium, high
}
}
struct TaskTimelineProvider: TimelineProvider {
// Placeholder während des initialen Ladens
func placeholder(in context: Context) -> TaskEntry {
TaskEntry(
date: Date(),
tasks: [
Task(id: UUID(), title: "Beispielaufgabe", isCompleted: false, priority: .medium)
]
)
}
// Snapshot für die Widget-Galerie
func getSnapshot(in context: Context, completion: @escaping (TaskEntry) -> Void) {
let entry = TaskEntry(
date: Date(),
tasks: TaskDataManager.shared.fetchTasks().prefix(3).map { $0 }
)
completion(entry)
}
// Vollständige Timeline mit Refresh-Policy
func getTimeline(in context: Context, completion: @escaping (Timeline<TaskEntry>) -> Void) {
let tasks = TaskDataManager.shared.fetchTasks()
let entry = TaskEntry(date: Date(), tasks: Array(tasks.prefix(3)))
// Aktualisierung in 15 Minuten oder nach Benutzeraktion
let nextUpdate = Calendar.current.date(
byAdding: .minute,
value: 15,
to: Date()
) ?? Date()
let timeline = Timeline(
entries: [entry],
policy: .after(nextUpdate)
)
completion(timeline)
}
}Der Provider verwendet einen gemeinsamen TaskDataManager für den Datenzugriff. Dieser Ansatz garantiert die Synchronisation zwischen Hauptanwendung und Widget.
Um Daten zwischen App und Widget zu teilen, muss eine App Group in den Projekt-Capabilities konfiguriert werden. UserDefaults oder Dateien müssen diese gemeinsame Gruppe nutzen.
Der Manager für Geteilte Daten
Der Datenaustausch zwischen Anwendung und Widget erfordert einen gemeinsamen Container, der über App Group zugänglich ist.
import Foundation
final class TaskDataManager {
// Singleton für globalen Zugriff
static let shared = TaskDataManager()
// App Group Bezeichner, in Xcode konfiguriert
private let appGroupID = "group.com.example.taskapp"
// UserDefaults zwischen App und Widget geteilt
private var sharedDefaults: UserDefaults? {
UserDefaults(suiteName: appGroupID)
}
private let tasksKey = "tasks"
private init() {}
// Aufgaben aus dem geteilten Speicher abrufen
func fetchTasks() -> [Task] {
guard let data = sharedDefaults?.data(forKey: tasksKey),
let tasks = try? JSONDecoder().decode([Task].self, from: data) else {
return []
}
return tasks
}
// Speichern mit Widget-Benachrichtigung
func saveTasks(_ tasks: [Task]) {
guard let data = try? JSONEncoder().encode(tasks) else { return }
sharedDefaults?.set(data, forKey: tasksKey)
}
// Spezifische Aufgabe aktualisieren
func updateTask(_ task: Task) {
var tasks = fetchTasks()
if let index = tasks.firstIndex(where: { $0.id == task.id }) {
tasks[index] = task
saveTasks(tasks)
}
}
// Erledigt-Status umschalten
func toggleTaskCompletion(taskID: UUID) {
var tasks = fetchTasks()
if let index = tasks.firstIndex(where: { $0.id == taskID }) {
tasks[index].isCompleted.toggle()
saveTasks(tasks)
}
}
}Dieser Manager kapselt die gesamte Persistenzlogik und wird sowohl von der Anwendung als auch von den Widget App Intents verwendet.
Erstellung des App Intent für Interaktivität
Der App Intent definiert die Aktion, die bei Benutzerinteraktion mit dem Widget ausgeführt wird. iOS führt diese Aktion im Hintergrund aus und aktualisiert das Widget anschließend automatisch.
import AppIntents
import WidgetKit
// Intent zum Umschalten des Aufgabenstatus
struct ToggleTaskIntent: AppIntent {
// Titel in Siri-Kurzbefehlen
static var title: LocalizedStringResource = "Aufgabenstatus umschalten"
// Beschreibung für Barrierefreiheit
static var description = IntentDescription("Markiert eine Aufgabe als erledigt oder nicht erledigt.")
// Parameter: ID der zu ändernden Aufgabe
@Parameter(title: "Aufgaben-ID")
var taskID: String
// Erforderlicher Initializer für AppIntent
init() {}
// Initializer mit Parameter zur Erstellung aus der View
init(taskID: UUID) {
self.taskID = taskID.uuidString
}
// Ausführung der Aktion
func perform() async throws -> some IntentResult {
// Konvertierung der String-ID in UUID
guard let uuid = UUID(uuidString: taskID) else {
return .result()
}
// Aktualisierung der Aufgabe
TaskDataManager.shared.toggleTaskCompletion(taskID: uuid)
// Widget-Aktualisierung anfordern
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result()
}
}Der Aufruf von WidgetCenter.shared.reloadTimelines löst eine sofortige Widget-Aktualisierung nach der Aktion aus und gewährleistet ein unmittelbares visuelles Feedback.
Bereit für deine iOS-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Widget-View mit Interaktiven Buttons
Die Widget-View verwendet die Standard-SwiftUI-Komponente Button mit dem Intent als Aktion. iOS 17+ fängt diese Interaktionen automatisch ab, um den App Intent auszuführen.
import SwiftUI
import WidgetKit
struct TaskWidgetView: View {
let entry: TaskEntry
// Anpassung an die Widget-Größe
@Environment(\.widgetFamily) var family
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// Header mit Titel und Zähler
headerView
// Aufgabenliste mit interaktiven Buttons
ForEach(entry.tasks.prefix(tasksLimit)) { task in
TaskRowView(task: task)
}
Spacer(minLength: 0)
}
.padding()
}
// Anzahl der Aufgaben je nach Größe
private var tasksLimit: Int {
switch family {
case .systemSmall: return 2
case .systemMedium: return 3
default: return 4
}
}
private var headerView: some View {
HStack {
Text("Aufgaben")
.font(.headline)
.fontWeight(.bold)
Spacer()
// Badge mit der Anzahl verbleibender Aufgaben
let remaining = entry.tasks.filter { !$0.isCompleted }.count
Text("\(remaining)")
.font(.caption.bold())
.foregroundStyle(.white)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(remaining > 0 ? Color.orange : Color.green)
.clipShape(Capsule())
}
}
}
struct TaskRowView: View {
let task: Task
var body: some View {
// Button mit App Intent als Aktion
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack(spacing: 12) {
// Erledigt-Indikator
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.font(.title3)
.foregroundStyle(task.isCompleted ? .green : .secondary)
// Aufgabentitel
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
.foregroundStyle(task.isCompleted ? .secondary : .primary)
.lineLimit(1)
Spacer()
// Prioritätsindikator
priorityIndicator
}
.padding(.vertical, 6)
.padding(.horizontal, 10)
.background(Color(.systemBackground).opacity(0.5))
.cornerRadius(8)
}
.buttonStyle(.plain)
}
@ViewBuilder
private var priorityIndicator: some View {
switch task.priority {
case .high:
Image(systemName: "exclamationmark.circle.fill")
.foregroundStyle(.red)
case .medium:
Image(systemName: "minus.circle.fill")
.foregroundStyle(.orange)
case .low:
EmptyView()
}
}
}Die Syntax Button(intent:) verbindet den Button direkt mit dem App Intent. Bei Berührung führt iOS perform() aus und aktualisiert das Widget anschließend automatisch.
Interaktiver Toggle für Widgets
Für Aktionen vom Typ Ein/Aus bietet die Toggle-Komponente eine Alternative zum Button mit nativem iOS-Stil.
import SwiftUI
import AppIntents
// Spezifischer Intent für Toggle mit explizitem Status
struct SetTaskCompletionIntent: AppIntent {
static var title: LocalizedStringResource = "Aufgabenstatus festlegen"
@Parameter(title: "Aufgaben-ID")
var taskID: String
// Zielstatus: true = erledigt, false = nicht erledigt
@Parameter(title: "Erledigt")
var isCompleted: Bool
init() {}
init(taskID: UUID, isCompleted: Bool) {
self.taskID = taskID.uuidString
self.isCompleted = isCompleted
}
func perform() async throws -> some IntentResult {
guard let uuid = UUID(uuidString: taskID) else {
return .result()
}
var tasks = TaskDataManager.shared.fetchTasks()
if let index = tasks.firstIndex(where: { $0.id == uuid }) {
// Status explizit setzen (nicht umschalten)
tasks[index].isCompleted = isCompleted
TaskDataManager.shared.saveTasks(tasks)
}
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result()
}
}
struct TaskToggleRowView: View {
let task: Task
var body: some View {
HStack {
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
Spacer()
// Interaktiver Toggle mit Intent
Toggle(
isOn: task.isCompleted,
intent: SetTaskCompletionIntent(
taskID: task.id,
isCompleted: !task.isCompleted
)
)
.toggleStyle(.switch)
.labelsHidden()
}
.padding(.vertical, 4)
}
}Der Toggle bietet eine intuitivere Interaktion für binäre Zustände und integriert sich nahtlos in iOS-Designs.
Widgets können keine Alerts, Sheets oder Navigation anzeigen. Alle Aktionen müssen autonom sein und den sichtbaren Status direkt aktualisieren.
Refresh-Animationen und Übergänge
iOS 17+ ermöglicht die Animation von Übergängen während der Widget-Aktualisierung nach einer Aktion. Der Modifier .contentTransition steuert diese Animationen.
import SwiftUI
import WidgetKit
struct AnimatedTaskRowView: View {
let task: Task
var body: some View {
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack(spacing: 12) {
// Icon mit Übergangsanimation
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.font(.title3)
.foregroundStyle(task.isCompleted ? .green : .secondary)
// Icon-Animation bei Änderung
.contentTransition(.symbolEffect(.replace))
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
.foregroundStyle(task.isCompleted ? .secondary : .primary)
// Text-Animation
.contentTransition(.opacity)
Spacer()
}
.padding(.vertical, 6)
.padding(.horizontal, 10)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(task.isCompleted ? Color.green.opacity(0.1) : Color.clear)
)
// Hintergrund-Animation
.animation(.easeInOut(duration: 0.3), value: task.isCompleted)
}
.buttonStyle(.plain)
}
}
// Widget mit animierter Invalidierung
struct AnimatedTaskWidget: Widget {
let kind: String = "AnimatedTaskWidget"
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: TaskTimelineProvider()
) { entry in
AnimatedTaskWidgetView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Animierte Aufgaben")
.description("Widgets mit weichen Animationen.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
// Aktivierung der Inhaltsanimationen
.contentMarginsDisabled()
}
}
struct AnimatedTaskWidgetView: View {
let entry: TaskEntry
var body: some View {
VStack(alignment: .leading, spacing: 8) {
headerView
ForEach(entry.tasks) { task in
AnimatedTaskRowView(task: task)
}
Spacer(minLength: 0)
}
.padding()
}
private var headerView: some View {
HStack {
Text("Aufgaben")
.font(.headline.bold())
Spacer()
let completed = entry.tasks.filter(\.isCompleted).count
let total = entry.tasks.count
// Animierter Fortschritt
Text("\(completed)/\(total)")
.font(.caption.bold())
.foregroundStyle(.secondary)
.contentTransition(.numericText())
}
}
}Die Animationen .symbolEffect(.replace) und .numericText() erzeugen weiche Übergänge zwischen Zuständen und verbessern die Benutzererfahrung erheblich.
Konfigurierbares Widget mit AppIntentConfiguration
Für vom Benutzer anpassbare Widgets (Filter, Kategorien) ersetzt AppIntentConfiguration die StaticConfiguration.
import WidgetKit
import SwiftUI
import AppIntents
// Dem Benutzer zugängliche Konfiguration
struct TaskWidgetConfigurationIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Aufgabenkonfiguration"
static var description = IntentDescription("Passen Sie die Aufgabenanzeige an.")
// Filter nach Priorität
@Parameter(title: "Priorität", default: .all)
var priorityFilter: PriorityFilter
// Erledigte Aufgaben anzeigen
@Parameter(title: "Erledigte anzeigen", default: true)
var showCompleted: Bool
// Maximale Anzahl von Aufgaben
@Parameter(title: "Anzahl Aufgaben", default: 3)
var maxTasks: Int
}
// Enum für Prioritätsfilter
enum PriorityFilter: String, AppEnum {
case all
case high
case medium
case low
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Priorität"
static var caseDisplayRepresentations: [PriorityFilter: DisplayRepresentation] = [
.all: "Alle",
.high: "Hoch",
.medium: "Mittel",
.low: "Niedrig"
]
}
// An die Konfiguration angepasster Provider
struct ConfigurableTaskProvider: AppIntentTimelineProvider {
typealias Entry = TaskEntry
typealias Intent = TaskWidgetConfigurationIntent
func placeholder(in context: Context) -> TaskEntry {
TaskEntry(date: Date(), tasks: [])
}
func snapshot(for configuration: TaskWidgetConfigurationIntent, in context: Context) async -> TaskEntry {
let tasks = filteredTasks(for: configuration)
return TaskEntry(date: Date(), tasks: tasks)
}
func timeline(for configuration: TaskWidgetConfigurationIntent, in context: Context) async -> Timeline<TaskEntry> {
let tasks = filteredTasks(for: configuration)
let entry = TaskEntry(date: Date(), tasks: tasks)
let nextUpdate = Date().addingTimeInterval(15 * 60)
return Timeline(entries: [entry], policy: .after(nextUpdate))
}
// Konfigurationsfilter anwenden
private func filteredTasks(for config: TaskWidgetConfigurationIntent) -> [Task] {
var tasks = TaskDataManager.shared.fetchTasks()
// Filterung nach Priorität
if config.priorityFilter != .all {
let priority = Task.Priority(rawValue: config.priorityFilter.rawValue) ?? .medium
tasks = tasks.filter { $0.priority == priority }
}
// Filterung der Erledigten bei Bedarf
if !config.showCompleted {
tasks = tasks.filter { !$0.isCompleted }
}
// Anzahl begrenzen
return Array(tasks.prefix(config.maxTasks))
}
}
// Widget mit Benutzerkonfiguration
struct ConfigurableTaskWidget: Widget {
let kind: String = "ConfigurableTaskWidget"
var body: some WidgetConfiguration {
// AppIntentConfiguration für konfigurierbare Widgets
AppIntentConfiguration(
kind: kind,
intent: TaskWidgetConfigurationIntent.self,
provider: ConfigurableTaskProvider()
) { entry in
TaskWidgetView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Personalisierte Aufgaben")
.description("Filtern und personalisieren Sie Ihre Aufgaben.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}Der Benutzer kann das Widget nun durch langes Drücken konfigurieren und erhält so ein personalisiertes Erlebnis ohne zusätzlichen Code in der Anwendung.
Fehlerbehandlung und Ladezustände
Eine gute UX erfordert die Behandlung von Fehlerfällen und Zwischenzuständen während der Interaktionen.
import AppIntents
import WidgetKit
struct ToggleTaskWithFeedbackIntent: AppIntent {
static var title: LocalizedStringResource = "Aufgabe mit Feedback umschalten"
@Parameter(title: "Aufgaben-ID")
var taskID: String
init() {}
init(taskID: UUID) {
self.taskID = taskID.uuidString
}
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
guard let uuid = UUID(uuidString: taskID) else {
// Stiller Fehler zurückgeben
return .result(value: false)
}
// Asynchrone Operation simulieren (z.B. Server-Sync)
do {
try await Task.sleep(for: .milliseconds(100))
TaskDataManager.shared.toggleTaskCompletion(taskID: uuid)
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result(value: true)
} catch {
// Fehler: Widget nicht aktualisieren
return .result(value: false)
}
}
}
// View mit Ladezustand
struct TaskRowWithLoadingView: View {
let task: Task
@State private var isLoading = false
var body: some View {
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack(spacing: 12) {
// Bedingter Indikator
Group {
if isLoading {
ProgressView()
.scaleEffect(0.8)
} else {
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundStyle(task.isCompleted ? .green : .secondary)
}
}
.frame(width: 24, height: 24)
Text(task.title)
.font(.subheadline)
.strikethrough(task.isCompleted)
Spacer()
}
.padding(.vertical, 6)
.padding(.horizontal, 10)
.background(Color(.systemBackground).opacity(0.5))
.cornerRadius(8)
}
.buttonStyle(.plain)
.disabled(isLoading)
.opacity(isLoading ? 0.6 : 1.0)
}
}Unmittelbares visuelles Feedback (verringerte Deckkraft, Ladeindikator) signalisiert dem Benutzer, dass seine Aktion registriert wurde.
Best Practices und Optimierungen
Mehrere Muster gewährleisten leistungsstarke und zuverlässige interaktive Widgets.
import WidgetKit
import SwiftUI
// 1. Cache nach jeder Änderung immer invalidieren
final class WidgetRefreshManager {
static func refreshAllWidgets() {
// Aktualisierung aller App-Widgets
WidgetCenter.shared.reloadAllTimelines()
}
static func refreshWidget(kind: String) {
// Aktualisierung eines bestimmten Widgets
WidgetCenter.shared.reloadTimelines(ofKind: kind)
}
// Aufruf aus der App nach Datenänderung
static func notifyDataChanged() {
Task { @MainActor in
refreshAllWidgets()
}
}
}
// 2. Komplexität der Views begrenzen
struct OptimizedWidgetView: View {
let entry: TaskEntry
var body: some View {
// Einfache Views ohne GeometryReader bevorzugen
VStack(alignment: .leading, spacing: 8) {
ForEach(entry.tasks.prefix(3)) { task in
// Leichtgewichtige Komponenten
minimalTaskRow(task)
}
}
.padding()
}
// Minimale View für Performance
@ViewBuilder
private func minimalTaskRow(_ task: Task) -> some View {
Button(intent: ToggleTaskIntent(taskID: task.id)) {
HStack {
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
Text(task.title)
.lineLimit(1)
}
}
.buttonStyle(.plain)
}
}
// 3. @AppStorage für einfache Zustände verwenden
struct QuickSettingsWidgetView: View {
// Direkter Zugriff auf geteilte UserDefaults
@AppStorage("showCompletedTasks", store: UserDefaults(suiteName: "group.com.example.app"))
private var showCompleted = true
var body: some View {
// Status bleibt zwischen Aktualisierungen erhalten
Text(showCompleted ? "Alle anzeigen" : "Erledigte ausblenden")
}
}
// 4. Daten im Provider vorberechnen
struct OptimizedTaskEntry: TimelineEntry {
let date: Date
let tasks: [Task]
// Vorberechnete Daten
let completedCount: Int
let pendingCount: Int
let highPriorityCount: Int
init(date: Date, tasks: [Task]) {
self.date = date
self.tasks = tasks
// Berechnungen werden nur einmal durchgeführt
self.completedCount = tasks.filter(\.isCompleted).count
self.pendingCount = tasks.filter { !$0.isCompleted }.count
self.highPriorityCount = tasks.filter { $0.priority == .high && !$0.isCompleted }.count
}
}Diese Optimierungen sorgen für reaktionsschnelle Widgets, die den Akku nicht übermäßig belasten.
Verwenden Sie das Widget-Schema in Xcode für das Debugging. Die Canvas-Vorschau ermöglicht das Testen verschiedener Größen und Zustände ohne Geräteinstallation.
Fazit
WidgetKit iOS 17+ mit App Intents verwandelt Widgets in echte interaktive Erweiterungen von iOS-Anwendungen. Diese deklarative Architektur vereinfacht die Entwicklung erheblich und bietet eine native, flüssige Benutzererfahrung.
Checkliste Interaktives iOS 17+ Widget
- ✅ App Group für Datenaustausch konfigurieren
- ✅ Timeline Provider mit angemessener Aktualisierung erstellen
- ✅ App Intents für jede Aktion implementieren
- ✅
Button(intent:)oderToggle(intent:)für Interaktivität verwenden - ✅
WidgetCenter.shared.reloadTimelinesnach jeder Änderung aufrufen - ✅ Pflicht-
.containerBackgroundfür iOS 17+ hinzufügen - ✅ Weiche Übergangsanimationen implementieren
- ✅ Lade- und Fehlerzustände behandeln
- ✅ Views für Akkuleistung optimieren
- ✅ Auf allen unterstützten Widget-Größen testen
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Tags
Teilen
Verwandte Artikel

App Intents und Siri Shortcuts: fortgeschrittene iOS-Automatisierung 2026
Vollständiger Leitfaden zu App Intents und Siri Shortcuts für iOS 18+. Eigene Siri-Aktionen erstellen, Apple Intelligence integrieren und die Swift-App 2026 automatisieren.

Combine vs async/await in Swift: Progressive Migrationsmuster
Vollständiger Leitfaden zur Migration von Combine zu async/await in Swift: progressive Strategien, Bridging-Muster und Paradigmen-Koexistenz in iOS-Codebasen.

iOS-Accessibility-Interviewfragen 2026: VoiceOver und Dynamic Type
Vorbereitung auf iOS-Interviews mit zentralen Accessibility-Fragen: VoiceOver, Dynamic Type, semantische Traits und Audits.