In response to client curiosity in speaking with emojis and GIFs, an rising variety of firms are incorporating animated GIFs into their electronic mail campaigns, web sites, and cellular apps in an effort to extend engagement and gross sales.
Graphics Interchange Format recordsdata are a set of photos which might be performed in a sequence in order that they seem like transferring. GIFs can be utilized to share demos, spotlight product options or modifications, illustrate a use case, or showcase model character.
Many well-liked chat apps, like iMessage and WhatsApp, and social platforms, like Reddit or Twitter, help sending and receiving GIFs. So, what about iOS apps? Properly, as of this writing, there‘s no native inbuilt help to make use of GIFs in SwiftUI or UIKit.
Constructing an environment friendly GIF picture loader for iOS would take important effort and time, however thankfully, some third-party frameworks are performant and might show GIFs with none body delays.
On this article, we’ll reveal the right way to use a well-liked GIF library, FLAnimatedImage by Flipboard, so as to add GIFs to your iOS app with only a few traces of code. Within the demo portion of the article, we’ll make the most of GIFs from GIPHY, a well-liked database providing a variety of animated GIFs.
Let’s get began, and find out how we will embody some superb GIFs in our apps!
Soar forward:
Putting in FLAnimatedImage
Any of the next three dependency managers could also be used so as to add FLAnimatedImage to a venture:
- Cocoapods
- Carthage
- Swift Package deal Supervisor
CocoaPods
So as to add FLAnimatedImage to a venture utilizing CocoaPods, add the next code to the Podfile:
pod 'FLAnimatedImage'
Then, go to your terminal’s venture listing and set up the pod, like so:
pod set up
Carthage
So as to add FLAnimatedImage to a venture utilizing Carthage, add the next code within the Cartfile:
github "Flipboard/FLAnimatedImage"
Then, to replace solely this library, go to your terminal’s venture listing and run the next command:
carthage replace FLAnimatedImage
Swift Package deal Supervisor
To make use of Swift Package deal Supervisor so as to add FLAnimatedImage to a venture, open Xcode, go to the menu bar, and choose File > Add Packages. Subsequent, paste the next hyperlink within the venture URL area:
https://github.com/Flipboard/FLAnimatedImage
Then, click on on Subsequent and choose the venture goal to which the library must be added. Click on proceed, and also you’ve added FLAnimatedImage
to the venture!
Utilizing FLAnimatedImage with Goal-C
The instance within the GitHub repo for FLAnimatedImage is written in Goal-C:
#import "FLAnimatedImage.h" FLAnimatedImage *picture = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif"]]]; FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init]; imageView.animatedImage = picture; imageView.body = CGRectMake(0.0, 0.0, 100.0, 100.0); [self.view addSubview:imageView];
We wish to use FLAnimatedImage with the SwiftUI framework, however earlier than doing so, we must always perceive this library’s two primary courses:
FLAnimatedImage
FLAnimatedImageView
FLAnimatedImage
class
FLAnimatedImage
is a category that helps ship the frames in a performant manner. We use it to set the picture property of the FLAnimatedImageView
class.
To load a GIF picture, we convert the GIF right into a Knowledge
worth sort. FLAnimatedImage
has an initializer that accepts this Knowledge
:
comfort init(animatedGIFData information: Knowledge!)
After creating an occasion of a picture of the kind FLAnimatedImage
, we will use it to set the picture property of FLAnimatedImageView
.
imageView.animatedImage
FLAnimatedImageView
class
FLAnimatedImageView
is a subclass of UIImageView
and takes a FLAnimatedImage
.
class FLAnimatedImageView
FLAnimatedImageView
mechanically performs the GIF within the view hierarchy and stops when it’s eliminated. We will set the animated picture by accessing the animatedImage
property.
Utilizing FLAnimatedImage with Swift
Now that we’re accustomed to each FLAnimatedImage courses and their utilization, we will translate the above Goal-C instance into Swift like so:
let url = URL(string: "https://add.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_percent28largepercent29.gif")! let information = attempt! Knowledge(contentsOf: url) /// Creating an animated picture from the given animated GIF information let animatedImage = FLAnimatedImage(animatedGIFData: information) /// Creating the picture view let imageView = FLAnimatedImageView() /// Setting the animated picture to the picture view imageView.animatedImage = animatedImage imageView.body = CGRect(x: 0, y: 0, width: 100, peak: 100) view.addSubview(imageView)
This instance is related for UIKit. It’s very straightforward to make use of FLAnimatedImage with UIKit as a result of it‘s native. Nevertheless, to make use of FLAnimatedImage in SwiftUI, we now have to create a customized view profiting from UIViewRepresentable
, a wrapper for a UIKit view that we use to combine it into the SwiftUI view hierarchy.
So, let’s create our customized view to simplify working with FLAnimatedImage!
GIFView
We’ll create a customized view in SwiftUI known as GIFView
, which helps us load GIFs from native property and a distant URL.
Earlier than creating the customized view, let’s create an enum URLType
that defines two circumstances:
identify
: the identify of the native fileurl
: the URL of the GIF that should be fetched from a server
enum URLType { case identify(String) case url(URL) var url: URL? { swap self { case .identify(let identify): return Bundle.primary.url(forResource: identify, withExtension: "gif") case .url(let remoteURL): return remoteURL } } }
Within the above code, we will see there’s additionally a computed property url
that returns the native URL if we offer each the distant URL and the identify of the GIF.
We create a brand new file, GIFView.swift
. On this file, we’ll import the FLAnimatedImage
library:
import FLAnimatedImage
Subsequent, we create a struct
, GIFView, that conforms to the UIViewRepresentable
protocol. This construction has one initializer that takes the URL sort:
struct GIFView: UIViewRepresentable { personal var sort: URLType init(sort: URLType) { self.sort = sort } }
Then, we add two closures, an occasion of FLAnimatedImageView
and UIActivityIndicatorView
, to indicate an exercise indicator whereas the GIF masses:
personal let imageView: FLAnimatedImageView = { let imageView = FLAnimatedImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.layer.cornerRadius = 24 imageView.layer.masksToBounds = true return imageView }() personal let activityIndicator: UIActivityIndicatorView = { let activityIndicator = UIActivityIndicatorView() activityIndicator.translatesAutoresizingMaskIntoConstraints = false return activityIndicator }()
Within the above code, we specify a worth of 24
for the FLAnimatedImageView
nook radius, however we will customise this nonetheless we want.
The UIViewRepresentable
protocol has two required strategies that should be applied. The primary, makeUIView(context:)
, creates the view object, UIView
, and configures the preliminary state. The second, updateUIView(_:context:)
, updates the state of the required view.
Within the makeUIView(context:)
technique, we:
- Create a
UIView
- Add each views,
makeUIView(context:)
andupdateUIView(_:context:)
, to theUIView
as subviews - Activate the mandatory constraints
- Return the
UIView
Right here’s the code for the the makeUIView(context:)
technique:
func makeUIView(context: Context) -> UIView { let view = UIView(body: .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 }
Within the updateUIView(_:context:)
technique, we:
- Begin animating the exercise indicator
- Test if the URL is optionally available or not; if it’s optionally available, we return from the strategy
- Create an occasion of
Knowledge
that accommodates the contents of the URL - Create an occasion of
FLAnimatedImage
passing the animated GIF information - Cease the exercise indicator from animating and set the
FLAnimatedView
‘s animated picture property to the fetched picture
Right here’s the code for the updateUIView(_:context:)
technique:
func updateUIView(_ uiView: UIView, context: Context) { activityIndicator.startAnimating() guard let url = sort.url else { return } DispatchQueue.international().async { if let information = attempt? Knowledge(contentsOf: url) { let picture = FLAnimatedImage(animatedGIFData: information) DispatchQueue.primary.async { activityIndicator.stopAnimating() imageView.animatedImage = picture } } } }
The picture takes time to load the animated GIF date, so we run it within the background on a concurrent thread to keep away from blocking the UI. Then, we set the picture on the principle thread.
Right here’s the ultimate code for the GIFView
:
import SwiftUI import FLAnimatedImage struct GIFView: UIViewRepresentable { personal var sort: URLType init(sort: URLType) { self.sort = sort } personal let imageView: FLAnimatedImageView = { let imageView = FLAnimatedImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.layer.cornerRadius = 24 imageView.layer.masksToBounds = true return imageView }() personal let activityIndicator: UIActivityIndicatorView = { let activityIndicator = UIActivityIndicatorView() activityIndicator.translatesAutoresizingMaskIntoConstraints = false return activityIndicator }() } extension GIFView { func makeUIView(context: Context) -> UIView { let view = UIView(body: .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 = sort.url else { return } DispatchQueue.international().async { if let information = attempt? Knowledge(contentsOf: url) { let picture = FLAnimatedImage(animatedGIFData: information) DispatchQueue.primary.async { activityIndicator.stopAnimating() imageView.animatedImage = picture } } } } }
Now that we now have a view for utilizing FLAnimatedImage
with SwiftUI, it’s as straightforward to make use of it with SwiftUI because it was with UIKit.
Let’s discover two easy examples, in addition to a extra complicated, real-world instance.
Easy GIF demos: FLAnimatedImage and SwiftUI
We’ll begin with a easy instance of a GIF current within the property folder. First, obtain the GIF, happy-work-from-home
, that we’ll use on this instance.
We create GIFView
, utilizing the strategy described beforehand. Subsequent, all we now have to do is go within the identify of the GIF, like so:
struct ContentView: View { var physique: some View { GIFView(sort: .identify("happy-work-from-home")) .body(maxHeight: 300) .padding() } }
Operating this code on the simulator gives us with an animated GIF:
Right here’s one other instance utilizing the identical GIF, however fetching it from a distant URL:
struct ContentView: View { var physique: some View { GIFView(sort: .url(URL(string: "https://media.giphy.com/media/Dh5q0sShxgp13DwrvG/giphy.gif")!)) .body(maxHeight: 300) .padding() } }
If we run this code on the simulator, we’ll see an animated GIF. Word that whereas the GIF is being fetched, the display shows an exercise indicator:
Now, let’s transfer on to a extra complicated, real-world instance!
Complicated, real-world GIF demo
To take full benefit of FLAnimatedImage, let’s discover an instance the place we load many GIFs concurrently. This instance will reveal how performant this framework is below reminiscence strain by displaying and scrolling easily by way of numerous GIFs.
GIPHY is the largest market for GIFs. It additionally has a developer program that enables us to fetch their information utilizing the supplied APIs. We’ll use certainly one of its APIs for fetching the trending GIFs and displaying them in a listing.
First, create a GIPHY account right here. After logging in, click on on the Create New App and choose API. Then, enter your app identify and outline, and make sure to the agreements. Subsequent, click on the Create App button and pay attention to the API Key.
The trending endpoint returns a listing of essentially the most related and interesting content material for that day. It appears to be like like this:
http://api.giphy.com/v1/gifs/trending
For authentication, we go the API key as a parameter. We will additionally restrict the variety of GIFs in a single response by utilizing the restrict
parameter and offset
to get the subsequent set of responses.
The endpoint returns a large JSON file, however we’ll trim it down to make use of it for our necessities. To take action, create one other file, GIFs.swift
, and add the next code:
import Basis struct GIFs: Codable { let information: [GIF] } struct GIF: Codable, Identifiable { let id: String let title: String let photos: Photos } struct Photos: Codable { let authentic: GIFURL } struct GIFURL: Codable { let url: String }
The endpoint returns an array of GIF
, and every GIF
has a title and a picture URL.
Shifting on to creating the UI, we add a variable that shops the GIFs and retains observe of the offset
. The concept is that after we refresh the display with a pull-to-refresh gesture, it fetches the subsequent batch of information:
struct ContentView: View { @State personal var gifs: [GIF] = [] @State personal var offset = 0 }
Subsequent, we’ll add a technique to fetch the GIF information from the trending endpoint:
extension ContentView { personal func fetchGIFs() async { do { attempt await fetchGIFData(offset: offset) } catch { print(error) } } personal func fetchGIFData(for restrict: Int = 10, offset: Int) async throws { var parts = URLComponents() parts.scheme = "https" parts.host = "api.giphy.com" parts.path = "/v1/gifs/trending" parts.queryItems = [ .init(name: "api_key", value: "<API KEY>"), // <-- ADD THE API KEY HERE .init(name: "limit", value: "(limit)"), .init(name: "offset", value: "(offset)") ] guard let url = parts.url else { throw URLError(.badURL) } let (information, _) = attempt await URLSession.shared.information(from: url) gifs = attempt JSONDecoder().decode(GIFs.self, from: information).information } }
The above code performs the next actions:
- Creates the URL for the trending endpoint and provides the question parameters for
api_key
,restrict
, andoffset
- Fetches the info from the URL and decodes it utilizing the
GIFs
construction - Units the info to the variable
gifs
For the UI, we now have a listing that masses over the array of gifs
and reveals particular person GIFs within the GIFView
that takes the URL:
extension ContentView { var physique: some View { NavigationView { if gifs.isEmpty { VStack(spacing: 10) { ProgressView() Textual content("Loading your favourite GIFs...") } } else { Record(gifs) { gif in if let url = URL(string: gif.photos.authentic.url) { GIFView(sort: .url(url)) .body(minHeight: 200) .listRowSeparator(.hidden) } } .listStyle(.plain) .navigationTitle("GIPHY") } } .navigationViewStyle(.stack) .job { await fetchGIFs() } .refreshable { offset += 10 await fetchGIFs() } } }
When the view is refreshed by knocking down on the display, it will increase the offset
by 10
, fetches new GIFs, and updates the display.
Right here’s a display recording displaying what occurs when the view is refreshed:
With that, we’ve created a full performant app that reveals the trending GIFs from GIPHY!
Conclusion
We’d need to put in a variety of effort to create a performant animated UIImageView
for dealing with GIFs. Alternatively, we will reap the benefits of the present FLAnimatedImage framework, utilized by many well-liked apps similar to Fb, Instagram, Slack, and Telegram.
With the customized GIFView
, we solely want one line of code in SwiftUI to show superb GIFs in our app! Have enjoyable experimenting and coding! Animated GIFs are simply certainly one of some ways to improve your iOS app’s UX and UI.
LogRocket: Full visibility into your net and cellular apps
LogRocket is a frontend software monitoring answer that permits you to replay issues as in the event that they occurred in your personal browser. As an alternative of guessing why errors occur, or asking customers for screenshots and log dumps, LogRocket helps you to replay the session to shortly perceive what went fallacious. It really works completely with any app, no matter framework, and has plugins to log further context from Redux, Vuex, and @ngrx/retailer.
Along with logging Redux actions and state, LogRocket information console logs, JavaScript errors, stacktraces, community requests/responses with headers + our bodies, browser metadata, and customized logs. It additionally devices the DOM to document the HTML and CSS on the web page, recreating pixel-perfect movies of even essentially the most complicated single-page net and cellular apps.