How to use GIFs inside an iOS App without any Third Party Library using UIKit?
Asked Answered
A

4

6

I am currently working on a project which depends heavily on GIFs. However, I never worked with GIFs in Swift for iOS before an I am pretty lost.

The first thing I tried was to import some GIFs to my Assets.xcassets folder. I realised that using an ImageSet for GIFs does not work.

So my first Question: How do I import GIFs into my project? And should I use only one resolution or are three the better way like for images?

Then I already checked out some questions about presenting a GIF inside a UIImageView, programmatically and via Storyboard. As I figured out, this is probably best possible by using an extension for the UIImage class. However, the Swift 4 examples didn't work for me because of some reason.

So my second Question: How do I use the imported GIFs inside my project and display them inside a UIImageView programmatically?

Aeromechanics answered 26/5, 2020 at 15:7 Comment(1)
did you find anything i am facing same problemScibert
P
1

You have to manually decode GIF data, then you will be able to show it frame-by-frame. You will not have any simple answers here, bcs that's definitely very hard idea, try copy decoding algorithm from Third-Party and player or just use it. Something like SDWebImage will be the best option for you. Check SDAnimatedImagePlayer, SDWebAnimatedImage classes.

Pixie answered 26/5, 2020 at 16:43 Comment(0)
P
3

If your animated gif has a constant frame time for all frames then you can easily load it from your assets catalog without third party libraries with this:

extension UIImage {
    static func animatedGif(named: String, framesPerSecond: Double = 10) -> UIImage? {
        guard let asset = NSDataAsset(name: named) else { return nil }
        return animatedGif(from: asset.data, framesPerSecond: framesPerSecond)
    }

    static func animatedGif(from data: Data, framesPerSecond: Double = 10) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { return nil }
        let imageCount = CGImageSourceGetCount(source)
        var images: [UIImage] = []
        for i in 0 ..< imageCount {
            if let cgImage = CGImageSourceCreateImageAtIndex(source, i, nil) {
                images.append(UIImage(cgImage: cgImage))
            }
        }
        return UIImage.animatedImage(with: images, duration: Double(images.count) / framesPerSecond)
    }
}
Professionalism answered 19/5, 2022 at 12:4 Comment(2)
how to set it on imageviewScibert
Same as any other UIImage should work imageView.image = UIImage.animatedGif(named: "imageName") assuming you added the gif to assets catalog as data asset.Professionalism
P
1

You have to manually decode GIF data, then you will be able to show it frame-by-frame. You will not have any simple answers here, bcs that's definitely very hard idea, try copy decoding algorithm from Third-Party and player or just use it. Something like SDWebImage will be the best option for you. Check SDAnimatedImagePlayer, SDWebAnimatedImage classes.

Pixie answered 26/5, 2020 at 16:43 Comment(0)
Q
1

Use CGAnimateImageDataWithBlock or CGAnimateImageAtURLWithBlock after iOS 13.

extension UIImageView: SupportProduct {
    
    public func load(name: String, in bundle: Bundle = .main) {
        if let data = NSDataAsset(name: name, bundle: bundle)?.data {
            CGAnimateImageDataWithBlock(data as CFData, nil) { (_, cgImage, _) in
                self.image = UIImage(cgImage: cgImage)
            }
        }
    }
}
Quinquevalent answered 26/7, 2023 at 8:10 Comment(0)
D
1

In addition to yycking's answer (I can't comment because of reputation), you have to stop 'CGAnimateImageDataWithBlock' manually. If not, it will still be animating even if UIImageView is nil.

So the final code is:

extension UIImageView {
    func loadGIF(name: String, in bundle: Bundle = .main) {
        if let data = NSDataAsset(name: name, bundle: bundle)?.data {
            CGAnimateImageDataWithBlock(data as CFData, nil) { [weak self] (index, cgImage, stop) in
                // NOTE: without 'stop.pointee = true', even if self is nil, animating will not be stopped
                guard let self = self else {
                    stop.pointee = true
                    return
                }
                self.image = UIImage(cgImage: cgImage)
            }
        }
    }
}
Dismal answered 5/3 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.