Working with UIImageView - codepath/ios_guides GitHub Wiki
Overview
Typically images are displayed using the built-in UIImageView. UIImageView supports both displaying a single image as well as animating a series of images.
Usage
With Interface Builder it's pretty easy to add and configure a UIImageView. The first step is to drag the UIImageView onto your view.

Then open the UIImageView properties pane and select the image asset (assuming you have some images in your project). You can also configure how the underlying image is scaled to fit inside the UIImageView.

Scale Types
Supporting Multiple Screen Densities
Working with UIImages
Loading Images from the Network
The built-in UIImageView works great when the image is locally available, but does not have a built-in API for downloading an image over the network and assigning it to the view. The standard approach today is to use URLSession with async/await; for SwiftUI projects, AsyncImage does this in one line; for projects that want the convenience of a UIImageView extension (the role AFNetworking used to fill), AlamofireImage is the actively-maintained successor.
Using URLSession with async/await
URLSession.shared.data(from:) returns the downloaded Data and a URLResponse. Decoding the data into a UIImage and assigning it to the view is done on the main actor:
class MyViewController: UIViewController {
@IBOutlet weak var myImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let imageURL = URL(string: "https://i.imgur.com/tGbaZCY.jpg")!
Task { @MainActor in
do {
let (data, _) = try await URLSession.shared.data(from: imageURL)
if let image = UIImage(data: data) {
self.myImageView.image = image
}
} catch {
print("Failed to load image: \(error)")
}
}
}
}
The Task { @MainActor in ... } block keeps the image assignment on the main thread, which UIKit requires for any view update. URLSession.shared.data(from:) requires iOS 15+; for older deployment targets, fall back to the closure-based URLSession.shared.dataTask(with:) and hop back to the main queue inside the completion handler.
Using AsyncImage (SwiftUI)
If you're in SwiftUI, AsyncImage (iOS 15+) loads and displays an image from a URL in one line, with placeholder and error states handled for you:
AsyncImage(url: URL(string: "https://i.imgur.com/tGbaZCY.jpg")) { image in
image.resizable().scaledToFit()
} placeholder: {
ProgressView()
}
AsyncImage only renders in SwiftUI — for UIKit views you still need the URLSession approach above or a third-party library.
Using AlamofireImage
AlamofireImage is the image-loading companion to Alamofire and is the direct equivalent of AFNetworking's old UIImageView category. It is actively maintained — current release is 4.4, requires Xcode 16+ and iOS 10+, and ships built-in caching, image transitions, and image filters.
Add it via Swift Package Manager (https://github.com/Alamofire/AlamofireImage) or CocoaPods (pod 'AlamofireImage'), then:
import AlamofireImage
class MyViewController: UIViewController {
@IBOutlet weak var myImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://i.imgur.com/tGbaZCY.jpg")!
myImageView.af.setImage(withURL: url)
}
}
The library asynchronously downloads the image, caches it, and assigns it to the view once the request finishes.
Improving the User Experience
A few enhancements help when working with images pulled from the network.
Fading in an Image Loaded from the Network
It can be jarring for the user to have an image pop into place once it has finished downloading. Fading it in smooths the transition:
let imageURL = URL(string: "https://i.imgur.com/tGbaZCY.jpg")!
Task { @MainActor in
do {
let (data, _) = try await URLSession.shared.data(from: imageURL)
guard let image = UIImage(data: data) else { return }
self.myImageView.alpha = 0.0
self.myImageView.image = image
UIView.animate(withDuration: 0.3) {
self.myImageView.alpha = 1.0
}
} catch {
// handle failure
}
}
With AlamofireImage, the equivalent is a one-liner via the imageTransition parameter:
myImageView.af.setImage(
withURL: imageURL,
placeholderImage: nil,
imageTransition: .crossDissolve(0.3)
)
Loading a Low Resolution Image followed by a High Resolution Image
Since high-resolution images take longer to download, it is common to first show a low-resolution placeholder so the user sees something immediately, then upgrade to the full image as it becomes available. With async/await this is just two sequential downloads:
let smallURL = URL(string: smallImageUrl)!
let largeURL = URL(string: largeImageUrl)!
Task { @MainActor in
do {
// Show the small image first
let (smallData, _) = try await URLSession.shared.data(from: smallURL)
if let smallImage = UIImage(data: smallData) {
self.myImageView.alpha = 0.0
self.myImageView.image = smallImage
UIView.animate(withDuration: 0.3) {
self.myImageView.alpha = 1.0
}
}
// Then upgrade to the larger image when it finishes downloading
let (largeData, _) = try await URLSession.shared.data(from: largeURL)
if let largeImage = UIImage(data: largeData) {
self.myImageView.image = largeImage
}
} catch {
// Handle failure (e.g., show a default image)
}
}