How to display an animated GIF in SwiftUI
Originally Published on April 24, 2023
Republished on June 26, 2023
I started making a movie app for personal use and wanted it to show an old movie reel countdown GIF.
I wanted the animated GIF to display each time I opened the app while the initial data was loading.
There is a Swift package called FLAnimatedImage that makes it easy to drop in a GIF. To add the swift package in XCode, go to File, then Add Packages.
Enter the link https://github.com/Flipboard/FLAnimatedImage into the search bar on the top right of the window that pops up.
Click Add Package, then wait for XCode to load it in!
With the package installed, make a new file and call it GIFView.
You are going to make a UIViewRepresentable, which is a way to display standard Swift code inside a SwiftUI app.
We can make this work in two ways: one would be a file that you add to XCode and another would be with a URL.
To start, we are going to create an enum to pass into the View.
enum URLType
case name(String)
case url(URL)
var url: URL? {
switch self {
case .name(let name):
return Bundle.main.url(forResource: name, withExtension: "GIF")
case .url(let remoteURL):
return remoteURL
}
}
}
Setting this will tell our View how to load the GIF we want to use.
Now, for our UIViewRepresentable, pass in this code.
import FLAnimatedImag
//https://github.com/Flipboard/FLAnimatedImage <-- SPM link for copying later if needed
struct GIFView: UIViewRepresentable {
private var type: URLType
init(type: URLType) {
self.type = type
}
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
view.addSubview(activityIndicator)
view.addSubview(imageView)
imageView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
imageView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
activityIndicator.startAnimating()
guard let url = type.url else { return }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
let image = FLAnimatedImage(animatedGIFData: data)
DispatchQueue.main.async {
activityIndicator.stopAnimating()
imageView.animatedImage = image
}
}
}
}
private let imageView: FLAnimatedImageView = {
let imageView = FLAnimatedImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
// UNCOMMENT TO ADD ROUNDING TO YOUR VIEW
// imageView.layer.cornerRadius = 24
imageView.layer.masksToBounds = true
return imageView
}()
private let activityIndicator: UIActivityIndicatorView = {
let activityIndicator = UIActivityIndicatorView()
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
activityIndicator.color = .systemBlue
return activityIndicator
}()
}
This code block will start by setting the enum type we just made in the init() method, and then load the GIF data into the ImageView. It also provides an activity indicator for GIFs loaded from the web.
To use this in your app, add GIFView in like you would any other SwiftUI View.
Here is a code sample using it in ContentView, showing how to load a GIF from an XCode file:
struct ContentView: View
var body: some View {
ZStack {
Color.black
VStack (alignment: .center) {
Spacer()
GIFView(type: .name("movieReel"))
.background(Color.black)
.frame(maxHeight: 300)
Spacer()
}
}
}
}
If you want to load it from a URL, initialize GIFView like this:
GIFView(type: .url(URL(string: "https://i.makeaGIF.com/media/3-28-2018/jLmnQr.GIF")!))
That should be it. Have fun adding GIFs to your app!
Here is a video of it in my app for anyone who isnβt going to code it.