AsyncImage. Loading images in SwiftUI
The article is now available on my blog:
Note: Examples are tested on iOS 15.0 with Xcode 13.0 beta (13A5154h).
iOS 15.0 beta 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.