Skip to content

Lightweight, performant, and memory efficient Gif framework

License

Notifications You must be signed in to change notification settings

BatyrOvezdurdyyev/JellyGif

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Language Platform Pod License Cover Image

Lightweight, performant, and memory efficient Gif framework

Features

  • Honor Gif frame duration
  • Optimized for CPU and Memory performance
  • Designed with UITableViewCell and UICollectionViewCell in mind
  • Full control over even the smallest details i.e preparation process, animation quality, output image quality, etc...
  • Extensible and easy to use

Installation

With CocoaPods

#source 'https://cocoapods.org/pods/JellyGif'

use_frameworks!
pod 'JellyGif'

Usage

The easiest way to get started is using JellyGifImageView and calling startGif(with:)

import JellyGif

let imageView = JellyGifImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

//Animates Gif from the main bundle
imageView.startGif(with: .name("Gif name"))

//Animates Gif with a local path
let url = URL(string: "Gif path")!
imageView.startGif(with: .localPath(url))

//Animates Gif with data
imageView.startGif(with: .data(Data))

To fully utilize full power of JellyGif, use JellyGifAnimator and conform to its JellyGifAnimatorDelegate

import JellyGif

let imageView = UIImageView(CGRect(x: 0, y: 0, width: 100, height: 100))
let animator = JellyGifAnimator(imageInfo: .name("Gif name"), pixelSize: .custom(350), animationQuality: .best)
animator.delegate = self

//JellyGifAnimatorDelegate
func gifAnimatorIsReady(_ sender: JellyGifAnimator) {
  sender.startAnimation()
}

func imageViewForAnimator(_ sender: JellyGifAnimator) -> UIImageView? { 
  return imageView
}

func gifAnimatorDidChangeImage(_ image: UIImage, sender: JellyGifAnimator) {
  //Use this method if you want to manually update Gif frame instead of using an UIImageView
}

JellyGifAnimator let you control every aspect of a Gif including its maximum output size - pixelSize and its frames per second - animationQuality. The closer the pixelSize property is to the actual size of the image holder the smaller the memory footprint and the better the CPU performance.

UICollectionView & UITableView

To use JellyGifAnimator with an UICollectionView or an UITableView, create a dictionary of JellyGifAnimator inside the owner of the UICollectionView or UITableView and conform to JellyGifAnimatorDelegate

import JellyGif

class ViewController: UIViewController {
  var gifNames: [String] = []
  var animators: [IndexPath: JellyGifAnimator] = [:]
    
  //Your code
  //...
}

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  //Your Code
  //...
  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    //dequeue cell
    //...
    
    let gifName = gifNames[indexPath.item]
    
    if animators[indexPath] == nil {
        let animator = JellyGifAnimator(imageInfo: .name(gifName), pixelSize: .custom(350), animationQuality: .best)
        animators[indexPath] = animator
    }

    //If the Gif is not ready, show a placeholder image - which is the first frame of the Gif instead
    if animators[indexPath]?.isReady != true {
        cell.imageView.image = animators[indexPath]?.placeholder
    }

    animators[indexPath]?.delegate = self

    return cell
  }
  
  func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if animators[indexPath]?.isReady == true {
      animators[indexPath]?.startAnimation()
    } else {
      animators[indexPath]?.prepareAnimation()
    }
  }

  func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    //Pause animation and the preparation process if the cell is not visible
    animators[indexPath]?.pauseAnimation()
    animators[indexPath]?.stopPreparingAnimation()
  }
}

extension ViewController: JellyGifAnimatorDelegate {
  func gifAnimatorIsReady(_ sender: JellyGifAnimator) {
    sender.startAnimation()
  }

  func imageViewForAnimator(_ sender: JellyGifAnimator) -> UIImageView? { 
    for indexPath in collectionView.indexPathsForVisibleItems {
      if animators[indexPath] === sender {
        return (collectionView.cellForItem(at: indexPath) as? YourCustomCell)?.imageView
      }
    }
    return nil
  }
}

Benchmark

Displays 1 image

CPU Usage Memory Usage
SwiftyGif 2% 44.6Mb
Gifu 2% 46.3Mb
JellyGif 1% 46.3Mb
JellyGif (optimized mode on) 1% 30.3Mb

Displays 10 images

CPU Usage Memory Usage
SwiftyGif 34% 31.7Mb
Gifu 6% 200Mb
JellyGif 6% 200Mb
JellyGif (optimized mode on) 5% 39.1Mb

Measured on an iPhone Xs Max, iOS 13.3.1 and Xcode 11.3.1

Compatibility

  • iOS 9.0+
  • Swift 5.0
  • Xcode 10+

About

Lightweight, performant, and memory efficient Gif framework

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 92.9%
  • Ruby 4.4%
  • Objective-C 2.7%