Wednesday, June 29, 2022
HomeWeb DevelopmentAdd GIFs to iOS apps with FLAminatedImage and SwiftUI

Add GIFs to iOS apps with FLAminatedImage and SwiftUI


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 file
  • url: 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:) and updateUIView(_:context:), to the UIView 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:

No Idea 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:

Activity 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, and offset
  • 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:

Scrolling Giphy and Refreshing GIFs

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.

: 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.

.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments