95

I have a String with an URL of GIF banner which I need to put into app.

My code:

func showAdd(){
    Request.get("http://www.kyst.no/api/?apiMode=advertisement&lang=no", { (error: NSError?, data: NSData, text: NSString?) -> () in
        let jsonResult: Dictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as Dictionary<String, AnyObject>
        var banner : NSString = jsonResult["advertisement"]!["banner"] as NSString
        self.addViewImage.image = UIImage.animatedImageNamed(banner, duration: 1)
    })
}

But nothing happens. Please help.

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Marius
  • 964
  • 1
  • 7
  • 9
  • `animatedImageNamed()` doesn't do what you think it does. That method is a shortcut for loading a series of static images that are named like so `image0`, `image 1`, `image2` and so on and sets them as the `animationImages` array. It does not load an animated image like a GIF. This question has been asked before and has a few answers: http://stackoverflow.com/questions/9744682/display-animated-gif-in-ios -- given it's not Swift, but it shouldn't be difficult to apply the answers to Swift. – Jasarien Jan 13 '15 at 12:17
  • If you don't want to use the open source ObjC code in answer linked above, then the basic gist is that you will need to decode the gif file to extract the individual frames, and then pass those frame images to the image view's `animationImages` property. – Jasarien Jan 13 '15 at 12:19
  • I've also tried http://expertland.net/question/a6bc48r3n7t5251n4v1p7l4n5u50jp6s9/detail.html But I get stuck on: var testImage = UIImage.animatedImageWithAnimatedGIFData(NSData.dataWithContentsOfURL(url)) 'dataWithContentsOfURL' is unavailable: use object construction 'NSData(contentsOfURL:)' – Marius Jan 13 '15 at 12:40
  • you can use this one: https://github.com/kirualex/SwiftyGif – Sour LeangChhean Mar 23 '17 at 08:59
  • please refer to this https://stackoverflow.com/a/50517364/3827479 – Sukwon Mar 09 '19 at 00:35

9 Answers9

175

Load GIF image Swift :

## Reference.

#1 : Copy the swift file from This Link :

#2 : Load GIF image Using Name

    let jeremyGif = UIImage.gifImageWithName("funny")
    let imageView = UIImageView(image: jeremyGif)
    imageView.frame = CGRect(x: 20.0, y: 50.0, width: self.view.frame.size.width - 40, height: 150.0)
    view.addSubview(imageView)

#3 : Load GIF image Using Data

    let imageData = try? Data(contentsOf: Bundle.main.url(forResource: "play", withExtension: "gif")!)
    let advTimeGif = UIImage.gifImageWithData(imageData!)
    let imageView2 = UIImageView(image: advTimeGif)
    imageView2.frame = CGRect(x: 20.0, y: 220.0, width: 
    self.view.frame.size.width - 40, height: 150.0)
    view.addSubview(imageView2)

#4 : Load GIF image Using URL

    let gifURL : String = "http://www.gifbin.com/bin/4802swswsw04.gif"
    let imageURL = UIImage.gifImageWithURL(gifURL)
    let imageView3 = UIImageView(image: imageURL)
    imageView3.frame = CGRect(x: 20.0, y: 390.0, width: self.view.frame.size.width - 40, height: 150.0)
    view.addSubview(imageView3)

Download Demo Code

OUTPUT :

iPhone 8 / iOS 11 / xCode 9

enter image description here

Kirit Modi
  • 23,155
  • 15
  • 89
  • 112
43

Simple extension for local gifs. Gets all the images from the gif and adds it to the imageView animationImages.

extension UIImageView {
    static func fromGif(frame: CGRect, resourceName: String) -> UIImageView? {
        guard let path = Bundle.main.path(forResource: resourceName, ofType: "gif") else {
            print("Gif does not exist at that path")
            return nil
        }
        let url = URL(fileURLWithPath: path)
        guard let gifData = try? Data(contentsOf: url),
            let source =  CGImageSourceCreateWithData(gifData as CFData, nil) else { return nil }
        var images = [UIImage]()
        let imageCount = CGImageSourceGetCount(source)
        for i in 0 ..< imageCount {
            if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
                images.append(UIImage(cgImage: image))
            }
        }
        let gifImageView = UIImageView(frame: frame)
        gifImageView.animationImages = images
        return gifImageView
    }
}

To Use:

 guard let confettiImageView = UIImageView.fromGif(frame: view.frame, resourceName: "confetti") else { return }
 view.addSubview(confettiImageView)
 confettiImageView.startAnimating()

Repeat and duration customizations using UIImageView APIs.

confettiImageView.animationDuration = 3
confettiImageView.animationRepeatCount = 1

When you are done animating the gif and want to release the memory.

confettiImageView.animationImages = nil
Mocha
  • 2,035
  • 12
  • 29
  • 2
    I used this approach to load a few gifs in a collectionView and my app was using over 2GB of memory. The use of gifs like this appears to be extremely memory intensive. – DoesData Mar 03 '19 at 17:45
  • 1
    cause `CGImageSourceCreateWithData` needs to be balanced with a `CFRelease` – rage Mar 07 '19 at 15:29
  • @rage How do I release the image? When I try to release it, xCode gives me warning saying CF objects are automatically memory managed. – Mocha Apr 01 '19 at 18:34
  • 2
    When you're done with your animation, do `confettiImageView.animationImages = nil` which will release the memory. – cbiggin Oct 09 '19 at 20:36
  • 1
    Down scale each frame of gif while extracting could be helpful in case of high memory usage, try specify option with `kCGImageSourceThumbnailMaxPixelSize` key in `CGImageSourceCreateImageAtIndex()` call, typically choose a size which equals to UI component's point size multiplied by device screen's scale factor. – Ke Yang Jun 25 '20 at 14:35
18

First install a pod :-

pod 'SwiftGifOrigin'

and import in your class

import SwiftGifOrigin

then write this code in viewDidiload method

yourImageView.image = UIImage.gif(name: "imageName")

Note:- plz do not include the file extension in the gif file name. Ex:-

//Don't Do this
yourImageView.image = UIImage.gif(name: "imageName.gif")

See source: https://github.com/swiftgif/SwiftGif

yoninja
  • 1,952
  • 2
  • 31
  • 39
Sagar Sangani
  • 287
  • 2
  • 7
  • 7
    There is no explanation for how this library works - how is somebody supposed to continue displaying a GIF image if this library suddenly starts to fail, or becomes corrupt, or if they have to otherwise remove it? Please attempt to explain what the library does. – royalmurder Jan 03 '19 at 11:28
  • not working for me Error : SwiftGif: This image named "test" does not exist I added test.gif in assets I can see image but I get above error – karan Feb 14 '19 at 10:30
  • 1
    @karan, use this: `imageView.loadGif(asset: "test")` – yoninja Mar 14 '19 at 11:36
  • I used only one file SwiftGifCommon/UIImage+Gif.swift and loadGif method. no need to install complete pod – Manish Nahar Aug 28 '19 at 04:13
  • It takes lot of memory when i play gif imageview in arkit scene due to that app UI freezed any solution? – iOSDude Mar 03 '20 at 10:15
  • 3
    It's never a solution to add a pod to your project! – Gastón Antonio Montes Sep 04 '20 at 23:34
  • I'm using this pod but the issue is gif image is display fine on Xcode debugging, but not displaying in TestFlight build. – Vijay Parmar Jun 22 '21 at 10:13
  • It's a lot more helpful to try to provide a solution without the use of unnecessary libraries which no one really knows how they work, since most people won't take the time to read it's code and understand how it works before using it. – K.A.Q Jan 03 '22 at 12:12
11
import UIKit
import ImageIO

extension UIImage {

public class func gifImageWithData(data: NSData) -> UIImage? {
    guard let source = CGImageSourceCreateWithData(data, nil) else {
        print("image doesn't exist")
        return nil
    }
    
    return UIImage.animatedImageWithSource(source: source)
}

public class func gifImageWithURL(gifUrl:String) -> UIImage? {
    guard let bundleURL = NSURL(string: gifUrl)
        else {
            print("image named \"\(gifUrl)\" doesn't exist")
            return nil
    }
    guard let imageData = NSData(contentsOf: bundleURL as URL) else {
        print("image named \"\(gifUrl)\" into NSData")
        return nil
    }
    
    return gifImageWithData(data: imageData)
}

public class func gifImageWithName(name: String) -> UIImage? {
    guard let bundleURL = Bundle.main
        .url(forResource: name, withExtension: "gif") else {
            print("SwiftGif: This image named \"\(name)\" does not exist")
            return nil
    }
    
    guard let imageData = NSData(contentsOf: bundleURL) else {
        print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
        return nil
    }
    
    return gifImageWithData(data: imageData)
}

class func delayForImageAtIndex(index: Int, source: CGImageSource!) -> Double {
    var delay = 0.1
    
    let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
    let gifProperties: CFDictionary = unsafeBitCast(CFDictionaryGetValue(cfProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()), to: CFDictionary.self)
    
    var delayObject: AnyObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()), to: AnyObject.self)
    
    if delayObject.doubleValue == 0 {
        delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
    }
    
    delay = delayObject as! Double
    
    if delay < 0.1 {
        delay = 0.1
    }
    
    return delay
}

class func gcdForPair(a: Int?, _ b: Int?) -> Int {
    var a = a
    var b = b
    if b == nil || a == nil {
        if b != nil {
            return b!
        } else if a != nil {
            return a!
        } else {
            return 0
        }
    }
    
    if a! < b! {
        let c = a!
        a = b!
        b = c
    }
    
    var rest: Int
    while true {
        rest = a! % b!
        
        if rest == 0 {
            return b!
        } else {
            a = b!
            b = rest
        }
    }
}

class func gcdForArray(array: Array<Int>) -> Int {
    if array.isEmpty {
        return 1
    }
    
    var gcd = array[0]
    
    for val in array {
        gcd = UIImage.gcdForPair(a: val, gcd)
    }
    
    return gcd
}

class func animatedImageWithSource(source: CGImageSource) -> UIImage? {
    let count = CGImageSourceGetCount(source)
    var images = [CGImage]()
    var delays = [Int]()
    
    for i in 0..<count {
        if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
            images.append(image)
        }
        
        let delaySeconds = UIImage.delayForImageAtIndex(index: Int(i), source: source)
        delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
    }
    
    let duration: Int = {
        var sum = 0
        
        for val: Int in delays {
            sum += val
        }
        
        return sum
    }()
    
    let gcd = gcdForArray(array: delays)
    var frames = [UIImage]()
    
    var frame: UIImage
    var frameCount: Int
    for i in 0..<count {
        frame = UIImage(cgImage: images[Int(i)])
        frameCount = Int(delays[Int(i)] / gcd)
        
        for _ in 0..<frameCount {
            frames.append(frame)
        }
    }
    
    let animation = UIImage.animatedImage(with: frames, duration: Double(duration) / 1000.0)
    
    return animation
}
}

Here is the file updated for Swift 3

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
Zach Doster
  • 119
  • 1
  • 5
  • 4
    Memory usage with this one is abnormally high, with a single larger, 6mb gif the memory usage shot to 400MB. Actual device not simulator. Haven't tried many other gifs though. – Sam Bing Apr 02 '18 at 18:35
  • @SamBing I am also facing the memory issue with this code, have you managed to find any update or other alternatives to this? – Sujal May 29 '18 at 10:17
  • @Sujal I did turn to a library called Gifu, mostly because the way it buffers and handles memory it's way more efficient. Take a look at it, as i did go through a lot of libraries, you may just save some time. – Sam Bing May 29 '18 at 10:42
  • @SamBing I did a pod install for 'Gifu' (latest v2.0.1). I am getting an error in one of its classes, named FrameStore (line 101, prepareFrames method). Also, I think the library loads only local gifs, i need to load them from network calls. The full documentation is not available. Any suggestion? – Sujal May 29 '18 at 10:58
  • 1
    @Sujal it supports loading GIFS from Data, so you can fetch the Data first. As for the error i'm really not sure as i haven't encountered it. – Sam Bing May 29 '18 at 12:47
  • 2
    @SamBing I did try using Gifu in a separate project and got it working. Thanks. The project i am working on is using the library 'SDWebImage' so I updated the pod for gif support and had the gifs displayed, the memory consumption is fine as well. You can have a look into it as well :) – Sujal May 31 '18 at 03:50
  • Same issue it took more than 700 MB with this to play small gif any solution please without third party library?? – iOSDude Feb 27 '20 at 09:27
  • HI, My app UI freezed after using this in Arkit sceneview any solution please? – iOSDude Mar 03 '20 at 10:06
11

You can try this new library. JellyGif respects Gif frame duration while being highly CPU & Memory performant. It works great with UITableViewCell & UICollectionViewCell too. To get started you just need to

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))

For more information you can look at its README

Linh Ta
  • 593
  • 6
  • 11
9

it would be great if somebody told to put gif into any folder instead of assets folder

smoothumut
  • 3,423
  • 1
  • 25
  • 35
2

In SWIFT 5 you can load any .gif image without the use of any third pary. You have to just add this code

extension UIImage {
    class func gifImageWithData(_ data: Data) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
            return nil
        }
        
        let frameCount = CGImageSourceGetCount(source)
        var images: [UIImage] = []
        
        for i in 0..<frameCount {
            if let cgImage = CGImageSourceCreateImageAtIndex(source, i, nil) {
                let image = UIImage(cgImage: cgImage)
                images.append(image)
            }
        }
        
        return UIImage.animatedImage(with: images, duration: 0.0)
    }
}



import UIKit

// Assuming you have a UIImageView outlet named `gifImageView` in your view controller

guard let gifPath = Bundle.main.path(forResource: "example", ofType: "gif") else {
    print("Failed to find the GIF image.")
    return
}

guard let gifData = try? Data(contentsOf: URL(fileURLWithPath: gifPath)) else {
    print("Failed to load the GIF image data.")
    return
}

guard let gifImage = UIImage.gifImageWithData(gifData) else {
    print("Failed to create the GIF image.")
    return
}

// Set the loaded GIF image to the UIImageView
gifImageView.image = gifImage

In the above example, replace "example" with the name of your GIF file (without the file extension) and make sure the GIF file is added to your Xcode project and included in the target.

Note that the code relies on a helper method gifImageWithData(_:) to create a UIImage from the GIF data. Here's an extension that provides that method:

Kaushik Movaliya
  • 799
  • 11
  • 27
0

This is working for me

Podfile:

platform :ios, '9.0'
use_frameworks!

target '<Your Target Name>' do
pod 'SwiftGifOrigin', '~> 1.7.0'
end

Usage:

// An animated UIImage
let jeremyGif = UIImage.gif(name: "jeremy")

// A UIImageView with async loading
let imageView = UIImageView()
imageView.loadGif(name: "jeremy")

// A UIImageView with async loading from asset catalog(from iOS9)
let imageView = UIImageView()
imageView.loadGif(asset: "jeremy")

For more information follow this link: https://github.com/swiftgif/SwiftGif

Arjun
  • 1,477
  • 1
  • 13
  • 23
  • 2
    A lot of open issues, last code commit around a year ago... This seems to be more up to date: https://github.com/kaishin/Gifu – Starwave Dec 30 '19 at 14:36
  • 1
    There is already a user that has answered with using SwiftGiftOrigin – Mocha Apr 16 '20 at 22:22
0

If you are using Kingfisher you can use the AnimatedImageView.

Therefore, in the .xib in the Identity Inspector (right sidemenu) of the existing imageView, change the class to AnimatedImageView. Module is switching automatically to Kingfisher. Change the Class of the IBOutlet in the viewClass also to AnimatedImageView.

FrugalResolution
  • 568
  • 4
  • 18