Home

Awesome

Logo

Test GitHub release Carthage compatible Swift 5.0 platforms

Gifu adds protocol-based, performance-aware animated GIF support to UIKit. (It's also a prefecture in Japan).

Install

Swift Package Manager

Add the following to your Package.swift file:

let package = Package(
    dependencies: [
    .package(url: "https://github.com/kaishin/Gifu.git", from: "3.2.2")
    ],
)

Carthage

CocoaPods

How It Works

Gifu does not require using the built-in GIFImageView subclass. The Animator class does the heavy-lifting, while the GIFAnimatable protocol exposes the functionality to the view classes that conform to it, using protocol extensions.

The Animator has a FrameStore that only keeps a limited number of frames in-memory, effectively creating a buffer for the animation without consuming all the available memory. This approach makes loading large GIFs a lot more resource-friendly.

The figure below summarizes how this works in practice. Given an image containing 10 frames, Gifu will load the current frame (red), buffer the next two frames in this example (orange), and empty up all the other frames to free up memory (gray):

<img src="https://github.com/kaishin/Gifu/raw/master/gifu-figure.gif" width="300" />

Usage

There are two options that should cover any situation:

GIFAnimatable

The bread and butter of Gifu. Through protocol extensions, GIFAnimatable exposes all the APIs of the library, and with very little boilerplate, any class can conform to it.

class MyImageView: UIImageView, GIFAnimatable {
  public lazy var animator: Animator? = {
    return Animator(withDelegate: self)
  }()

  override public func display(_ layer: CALayer) {
    updateImageIfNeeded()
  }
}

That's it. Now MyImageView has access to all these methods and properties:

Furthermore, you can make any class GIF-animatable, starting with UIView subclasses:

class CustomAnimatedView: UIView, GIFAnimatable {
  public lazy var animator: Animator? = {
    return Animator(withDelegate: self)
  }()

  override public func display(_ layer: CALayer) {
    updateImageIfNeeded()
  }
}

You can also make UIKit classes conform using associated objects may you wish:

import UIKit
import Gifu

extension UIImageView: GIFAnimatable {
  private struct AssociatedKeys {
    static var AnimatorKey = "gifu.animator.key"
  }

  override open func display(_ layer: CALayer) {
    updateImageIfNeeded()
  }

  public var animator: Animator? {
    get {
      guard let animator = objc_getAssociatedObject(self, &AssociatedKeys.AnimatorKey) as? Animator else {
        let animator = Animator(withDelegate: self)
        self.animator = animator
        return animator
      }

      return animator
    }

    set {
      objc_setAssociatedObject(self, &AssociatedKeys.AnimatorKey, newValue as Animator?, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
}

Examples

The simplest way to get started is initializing a GIFAnimatable class in code or in a storyboard, then calling animate(:) on it.

let imageView = GIFImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
imageView.animate(withGIFNamed: "mugen") {
  print("It's animating!")
}

You can also prepare for the animation when the view loads and only start animating after a user interaction.

// In your view controller..

override func viewDidLoad() {
  super.viewDidLoad()
  imageView.prepareForAnimation(withGIFNamed: "mugen") {
    print("Ready to animate!")
  }
}

@IBAction func toggleAnimation(_ sender: AnyObject) {
  if imageView.isAnimatingGIF {
    imageView.stopAnimatingGIF()
  } else {
    imageView.startAnimatingGIF()
  }
}

If you are using a GIFAnimatable class in a table or collection view, you can call the prepareForReuse() method in your cell subclass:

override func prepareForReuse() {
  super.prepareForReuse()
  imageView.prepareForReuse()
}

Demo App

Clone or download the repository and open Demo/Demo.xcworkspace to check out the demo app.

Documentation

See the full API documentation.

Compatibility

License

See LICENSE.