【问题标题】:How to load GIF image in Swift?如何在 Swift 中加载 GIF 图像?
【发布时间】:2022-01-24 20:43:18
【问题描述】:

我有一个带有 GIF 横幅 URL 的字符串,我需要将其放入应用程序中。

我的代码:

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

但是什么也没发生。请帮忙。

【问题讨论】:

  • animatedImageNamed() 不会像您认为的那样做。该方法是加载一系列静态图像的快捷方式,这些图像命名为image0image 1image2 等,并将它们设置为animationImages 数组。它不会像 GIF 那样加载动画图像。这个问题之前有人问过,有几个答案:stackoverflow.com/questions/9744682/display-animated-gif-in-ios——考虑到它不是 Swift,但将答案应用于 Swift 应该不难。
  • 如果您不想在上面链接的答案中使用开源 ObjC 代码,那么基本要点是您需要解码 gif 文件以提取各个帧,然后传递那些框架图像到图像视图的animationImages 属性。
  • 我也试过expertland.net/question/a6bc48r3n7t5251n4v1p7l4n5u50jp6s9/… 但我卡住了: var testImage = UIImage.animatedImageWithAnimatedGIFData(NSData.dataWithContentsOfURL(url)) 'dataWithContentsOfURL' 不可用:使用对象构造 'NSData(contentsOfURL: )'
  • 你可以用这个:github.com/kirualex/SwiftyGif

标签: ios swift uiimageview uiviewanimation


【解决方案1】:

快速加载 GIF 图片:

## Reference.

#1 : 从This Link 复制 swift 文件 :

#2 : 使用名称加载 GIF 图片

    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 : 使用数据加载 GIF 图片

    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 : 使用 URL 加载 GIF 图片

    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

输出:

iPhone 8 / iOS 11 / xCode 9

【讨论】:

  • 对于未来的用户,这不适用于 JPG 或 PNG。如果您正在加载 URL 列表或不知道扩展名,它将失败。
  • 这个库占用了太多内存,
  • successGIF.image = UIImage.gifImageWithName("translation-successful") 就是这样使用的,只是名称试图显示 GIF。这里怎么才能只显示一次
  • @KiritModi 这个库使用了太多的内存并造成了很多内存泄漏
  • 如何调整动画速度?
【解决方案2】:

本地 gif 的简单扩展。从 gif 中获取所有图像并将其添加到 imageView 动画图像中。

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

使用:

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

使用 UIImageView API 进行重复和持续时间自定义。

confettiImageView.animationDuration = 3
confettiImageView.animationRepeatCount = 1

当您完成 gif 动画并想要释放内存时。

confettiImageView.animationImages = nil

【讨论】:

  • 我使用这种方法在 collectionView 中加载了一些 gif,而我的应用程序使用了超过 2GB 的内存。使用这样的 gif 似乎非常消耗内存。
  • 因为CGImageSourceCreateWithData 需要与CFRelease 平衡
  • @rage 如何发布图像?当我尝试释放它时,xCode 给我警告说 CF 对象是自动内存管理的。
  • 完成动画后,执行confettiImageView.animationImages = nil,这将释放内存。
  • 在提取时缩小 gif 的每一帧在内存使用率高的情况下可能会有所帮助,请尝试在 CGImageSourceCreateImageAtIndex() 调用中使用 kCGImageSourceThumbnailMaxPixelSize 键指定选项,通常选择等于 UI 组件点的大小尺寸乘以设备屏幕的比例因子。
【解决方案3】:

首先安装一个 pod:-

pod 'SwiftGifOrigin'

并在您的班级中导入

import SwiftGifOrigin

然后在 viewDidiload 方法中编写这段代码

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

注意:- 请不要在 gif 文件名中包含文件扩展名。例如:-

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

查看来源:https://github.com/swiftgif/SwiftGif

【讨论】:

  • 没有解释这个库是如何工作的——如果这个库突然开始失败,或者损坏,或者如果他们必须以其他方式删除它,那么有人应该如何继续显示 GIF 图像?请尝试解释图书馆的作用。
  • 不适合我错误:SwiftGif:这个名为“test”的图像不存在我在资产中添加了 test.gif 我可以看到图像但我得到了上述错误
  • @karan,使用这个:imageView.loadGif(asset: "test")
  • 我只使用了一个文件 SwiftGifCommon/UIImage+Gif.swift 和 loadGif 方法。无需安装完整的 pod
  • 在项目中添加 pod 绝不是一种解决方案!
【解决方案4】:
//
//  iOSDevCenters+GIF.swift
//  GIF-Swift
//
//  Created by iOSDevCenters on 11/12/15.
//  Copyright © 2016 iOSDevCenters. All rights reserved.
//
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
}
}

这是为 Swift 3 更新的文件

【讨论】:

  • 这个的内存使用率异常高,一个更大的 6mb gif 内存使用量猛增到 400MB。实际设备不是模拟器。不过还没有尝试过很多其他的 gif。
  • @SamBing 我也面临此代码的内存问题,您是否设法找到任何更新或其他替代方法?
  • @Sujal 我确实求助于一个名为 Gifu 的库,主要是因为它缓冲和处理内存的方式更有效。看看吧,因为我确实浏览了很多库,您可能会节省一些时间。
  • @Sujal 它支持从 Data 加载 GIFS,因此您可以先获取 Data。至于错误我真的不确定,因为我没有遇到过。
  • @SamBing 我确实尝试在一个单独的项目中使用 Gifu 并让它工作。谢谢。我正在处理的项目正在使用库“SDWebImage”,所以我更新了 pod 以支持 gif 并显示 gif,内存消耗也很好。你也可以看看它:)
【解决方案5】:

你可以试试this new library。 JellyGif 尊重 Gif 帧持续时间,同时具有高度的 CPU 和内存性能。它也适用于 UITableViewCell 和 UICollectionViewCell。要开始,您只需要

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

更多信息可以查看它的README

【讨论】:

    【解决方案6】:

    如果有人告诉将 gif 放入任何文件夹而不是 assets 文件夹,那就太好了

    【讨论】:

    • 没错!丢失超过 30 分钟
    【解决方案7】:

    这对我有用

    Pod 文件:

    platform :ios, '9.0'
    use_frameworks!
    
    target '<Your Target Name>' do
    pod 'SwiftGifOrigin', '~> 1.7.0'
    end
    

    用法:

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

    欲了解更多信息,请点击此链接:https://github.com/swiftgif/SwiftGif

    【讨论】:

    • 很多未解决的问题,最后一次代码提交大约是一年前...这似乎是最新的:github.com/kaishin/Gifu
    • 已经有用户使用 SwiftGiftOrigin 回答了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-25
    • 2016-08-29
    • 2015-06-18
    • 2017-04-06
    • 1970-01-01
    • 2014-09-24
    • 1970-01-01
    相关资源
    最近更新 更多