Das Problem
SwiftUI’s AsyncImage ist praktisch zum Laden von Remote-Bildern, hat aber eine überraschende Einschränkung: Du kannst den .resizable()-Modifier nicht darauf anwenden. Dieser Code kompiliert, verhält sich aber nicht wie erwartet:
// This does NOT work as intended
AsyncImage(url: imageURL)
.resizable() // Has no effect -- AsyncImage is not an Image
.aspectRatio(contentMode: .fill)
.frame(width: 200, height: 200)Der Grund ist, dass AsyncImage kein Image ist – es ist eine Container-View, die den Ladezustand verwaltet. Der .resizable()-Modifier ist nur auf Image definiert, sodass die Anwendung auf AsyncImage nur die generische View-Version aufruft, die nichts Nützliches tut.
Die Lösung
Die Lösung ist die Verwendung des phasenbasierten Initializers, der dir direkten Zugriff auf den zugrunde liegenden Image-Wert gibt, sobald das Laden abgeschlossen ist:
AsyncImage(url: imageURL) { phase in
switch phase {
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
case .failure:
Image(systemName: "photo")
.foregroundStyle(.secondary)
case .empty:
ProgressView()
@unknown default:
EmptyView()
}
}
.frame(width: 200, height: 200)
.clipped()Innerhalb des .success-Falls ist image ein echter Image-Wert, sodass .resizable() korrekt funktioniert. Das gibt dir auch Kontrolle über die Lade- und Fehlerzustände, was ohnehin die bessere Praxis ist.
Wann man manuell lädt
Wenn du die rohen Bilddaten brauchst – zum Beispiel um sie zu cachen, ihre Größe zu prüfen oder ein UIImage zu erstellen – möchtest du AsyncImage vielleicht ganz überspringen und mit URLSession laden. Aber für die meisten reinen Anzeigefälle deckt der phasenbasierte Initializer den Bedarf ohne zusätzliche Komplexität.
Das ist eine dieser SwiftUI-APIs, bei denen der einfache Initializer verlockend aussieht, aber in der Praxis zu kurz greift. Verwende standardmäßig die phasenbasierte Version, wann immer du bildspezifische Modifier brauchst.
