AsyncImage. Loading images in SwiftUI
3 min read • ––– views
iOS 15.0 gives us new SwiftUI views, and one of them is AsyncImage
. It loads and displays an image from the given URL.
Let's start with a basic example:
import SwiftUI
struct ContentView: View {
private let url = URL(string: "https://picsum.photos/200")
var body: some View {
AsyncImage(url: url)
}
}
By default, it shows a gray background and replaces it with the loaded image:
Optionally we can change scale
to use for the image. In the example below the image size will be reduced by half:
import SwiftUI
struct ContentView: View {
private let url = URL(string: "https://picsum.photos/200")
var body: some View {
AsyncImage(url: url, scale: 2)
}
}
To update the appearance of AsyncImage
, we can use an initializer with content and placeholder view builders. Here we able to modify a final image and show a custom placeholder view:
import SwiftUI
struct ContentView: View {
private let url = URL(string: "https://picsum.photos/200")
var body: some View {
AsyncImage(url: url) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
Image(systemName: "photo")
.imageScale(.large)
.foregroundColor(.gray)
}
.ignoresSafeArea()
}
}
If we want to handle an error state, we can use another initializer with AsyncImagePhase
. It's a simple enum with three cases: empty, success, and error.
import SwiftUI
struct ContentView: View {
private let url = URL(string: "https://picsum.photos/200")
var body: some View {
AsyncImage(url: url, content: view)
}
@ViewBuilder
private func view(for phase: AsyncImagePhase) -> some View {
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fit)
case .failure(let error):
VStack(spacing: 16) {
Image(systemName: "xmark.octagon.fill")
.foregroundColor(.red)
Text(error.localizedDescription)
.multilineTextAlignment(.center)
}
@unknown default:
Text("Unknown")
.foregroundColor(.gray)
}
}
}
Here we show a spinner during loading, resized image if loading is successful, and an error message if something is wrong.
To specify animations between phase changes, we can optionally add Transition
:
import SwiftUI
struct ContentView: View {
private let url = URL(string: "https://picsum.photos/200")
private let transaction: Transaction = .init(animation: .linear)
var body: some View {
AsyncImage(url: url,
transaction: transaction,
content: view)
}
...
}
And, of course, we can use AsyncImage
inside List
to show multiple images:
import SwiftUI
struct ContentView: View {
private let url = URL(string: "https://picsum.photos/200")
var body: some View {
List {
ForEach(0..<10) { _ in
AsyncImage(url: url,
content: view)
.listRowInsets(.init(.zero))
}
}
.listStyle(.plain)
}
...
}
If you want to play with AsyncImage by yourself, check out AsyncImageExample project on Github.