如何创建动画 GIF

Posted

技术标签:

【中文标题】如何创建动画 GIF【英文标题】:How to create an animated GIF 【发布时间】:2019-07-14 04:42:57 【问题描述】:

我想在 SwiftUI 中创建一个动画 GIF。

我试过了:

    将 GIF 添加到我的资产并通过 Image(“myGif”) 加载它 通过下载 GIF 图像创建 UIImage,并将其传递给Image 使用this script 创建一个UIImage,然后重复#2。

以上方法都不行,有没有人解决这个问题?

【问题讨论】:

ios 不显示动画 gif,那么为什么使用 SwiftUI 会改变呢? 我实际上不是在谈论 iOS,而是在谈论 Apple Watch(因此是“apple-watch”标签)。 WatchKit 为显示 GIF 提供了一个非常简单的界面,我正在尝试在 SwiftUI 中重新创建它 【参考方案1】:

您可以通过从UIView 创建UIViewRepresentable 来实现。 UIViewRepresentable 为您创建View,您可以在SwiftUI 类中使用它。

第一步:为 Gifview 创建UIView

class GIFPlayerView: UIView 
    private let imageView = UIImageView()

    convenience init(gifName: String)  
       self.init()
       let gif = UIImage.gif(asset: gifName)
       imageView.image = gif
       imageView.contentMode = .scaleAspectFit
       self.addSubview(imageView)
    

    override init(frame: CGRect) 
       super.init(frame: frame)
    

    required init?(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    override func layoutSubviews() 
        super.layoutSubviews()
        imageView.frame = bounds
    

第二步:为 GifView 创建UIViewRepresentable

struct GIFView: UIViewRepresentable 
    var gifName: String

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<GIFView>) 

    


    func makeUIView(context: Context) -> UIView 
        return GIFPlayerView(gifName: gifName)
    

第 3 步:使用 UIViewRepresentable 视图。

var body: some View 
    VStack 
        GIFView(gifName: "your gif name")
    

注意: 将 Gif 扩展为图像。参考:https://***.com/a/45554784

extension UIImageView 

    public func loadGif(name: String) 
        DispatchQueue.global().async 
            let image = UIImage.gif(name: name)
            DispatchQueue.main.async 
                self.image = image
            
        
    

    @available(iOS 9.0, *)
    public func loadGif(asset: String) 
        DispatchQueue.global().async 
            let image = UIImage.gif(asset: asset)
            DispatchQueue.main.async 
                self.image = image
            
        
    



extension UIImage 

    public class func gif(data: Data) -> UIImage? 
        // Create source from data
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else 
            print("SwiftGif: Source for the image does not exist")
            return nil
        

        return UIImage.animatedImageWithSource(source)
    

    public class func gif(url: String) -> UIImage? 
        // Validate URL
        guard let bundleURL = URL(string: url) else 
            print("SwiftGif: This image named \"\(url)\" does not exist")
            return nil
        

        // Validate data
        guard let imageData = try? Data(contentsOf: bundleURL) else 
            print("SwiftGif: Cannot turn image named \"\(url)\" into NSData")
            return nil
        

        return gif(data: imageData)
    

    public class func gif(name: String) -> UIImage? 
        // Check for existance of gif
        guard let bundleURL = Bundle.main
          .url(forResource: name, withExtension: "gif") else 
            print("SwiftGif: This image named \"\(name)\" does not exist")
            return nil
        

        // Validate data
        guard let imageData = try? Data(contentsOf: bundleURL) else 
            print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
            return nil
        

        return gif(data: imageData)
    

    @available(iOS 9.0, *)
    public class func gif(asset: String) -> UIImage? 
        // Create source from assets catalog
        guard let dataAsset = NSDataAsset(name: asset) else 
            print("SwiftGif: Cannot turn image named \"\(asset)\" into NSDataAsset")
            return nil
        

        return gif(data: dataAsset.data)
    

    internal class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double 
        var delay = 0.1

        // Get dictionaries
        let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
        let gifPropertiesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 0)
        defer 
            gifPropertiesPointer.deallocate()
        
        let unsafePointer = Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()
        if CFDictionaryGetValueIfPresent(cfProperties, unsafePointer, gifPropertiesPointer) == false 
            return delay
        

        let gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)

        // Get delay time
        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)
        

        if let delayObject = delayObject as? Double, delayObject > 0 
            delay = delayObject
         else 
            delay = 0.1 // Make sure they're not too fast
        

        return delay
    

    internal class func gcdForPair(_ lhs: Int?, _ rhs: Int?) -> Int 
        var lhs = lhs
        var rhs = rhs
        // Check if one of them is nil
        if rhs == nil || lhs == nil 
            if rhs != nil 
                return rhs!
             else if lhs != nil 
                return lhs!
             else 
                return 0
            
        

        // Swap for modulo
        if lhs! < rhs! 
            let ctp = lhs
            lhs = rhs
            rhs = ctp
        

        // Get greatest common divisor
        var rest: Int
        while true 
            rest = lhs! % rhs!

            if rest == 0 
                return rhs! // Found it
             else 
                lhs = rhs
                rhs = rest
            
        
    

    internal class func gcdForArray(_ array: [Int]) -> Int 
        if array.isEmpty 
            return 1
        

        var gcd = array[0]

        for val in array 
            gcd = UIImage.gcdForPair(val, gcd)
        

        return gcd
    

    internal class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? 
        let count = CGImageSourceGetCount(source)
        var images = [CGImage]()
        var delays = [Int]()

        // Fill arrays
        for index in 0..<count 
            // Add image
            if let image = CGImageSourceCreateImageAtIndex(source, index, nil) 
                images.append(image)
            

            // At it's delay in cs
            let delaySeconds = UIImage.delayForImageAtIndex(Int(index),
                source: source)
            delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
        

        // Calculate full duration
        let duration: Int = 
            var sum = 0

            for val: Int in delays 
                sum += val
            

            return sum
            ()

        // Get frames
        let gcd = gcdForArray(delays)
        var frames = [UIImage]()

        var frame: UIImage
        var frameCount: Int
        for index in 0..<count 
            frame = UIImage(cgImage: images[Int(index)])
            frameCount = Int(delays[Int(index)] / gcd)

            for _ in 0..<frameCount 
                frames.append(frame)
            
        

        // Heyhey
        let animation = UIImage.animatedImage(with: frames,
            duration: Double(duration) / 1000.0)

        return animation
    


【讨论】:

对不起,我应该在我的帖子中说得更清楚。这是针对 WatchOS 的。我通过采用类似的方法并使用 WkInterfaceInlineMovie 解决了这个问题 如何在swiftui中使用扩展? 您询问的是哪个组件的扩展? @q8yas 嗨 Vishal Patel,感谢您的响应,您放置的扩展名 extension UIImageView public func loadGif(name: String) DispatchQueue.global().async let image = UIImage.gif(name: name) DispatchQueue.main.async self.image = image ....................

以上是关于如何创建动画 GIF的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 中从 JPEG 创建动画 GIF(开发)

如何从多个 png 图像创建动画 gif 图像?

如何从一堆jpgs创建gif动画

通过 iOS 创建和导出动画 gif?

如何使用ScreenToGif创建一个GIF动画视频

如何在 .net 中创建动画 gif