如何创建动画 GIF [英] How to create an animated GIF

查看:26
本文介绍了如何创建动画 GIF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 SwiftUI 中创建动画 GIF.

我试过了:

  1. 向我的资产添加 GIF 并通过 Image(myGif")
  2. 加载它
  3. 通过下载 GIF 图片创建 UIImage,并将其传递给 Image
  4. 使用this脚本创建一个UIImage,然后重复#2.

以上都没有奏效,有人发现了吗?

解决方案

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

第一步:为 Gifview 创建 UIView

class GIFPlayerView: UIView {私人让 imageView = UIImageView()方便初始化(gifName:字符串){self.init()让 gif = UIImage.gif(资产:gifName)imageView.image = gifimageView.contentMode = .scaleAspectFitself.addSubview(imageView)}覆盖初始化(框架:CGRect){super.init(框架:框架)}需要初始化?(编码器:NSCoder){fatalError("init(coder:) 尚未实现")}覆盖 func layoutSubviews() {super.layoutSubviews()imageView.frame = 边界}}

Step2:为 GifView 创建 UIViewRepresentable

struct GIFView: UIViewRepresentable {var gifName: 字符串func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<GIFView>) {}func makeUIView(context: Context) ->界面视图{返回 GIFPlayerView(gifName: gifName)}}

步骤 3: 使用 UIViewRepresentable 视图.

var body: some View {虚拟堆栈{GIFView(gifName: "你的 gif 名称")}}

<块引用>

注意: Gif to Image 的扩展.参考:https://stackoverflow.com/a/45554784

扩展 UIImageView {public func loadGif(名称:字符串){DispatchQueue.global().async {让 image = UIImage.gif(name: name)DispatchQueue.main.async {self.image = 图像}}}@available(iOS 9.0, *)public func loadGif(资产:字符串){DispatchQueue.global().async {让图像 = UIImage.gif(资产:资产)DispatchQueue.main.async {self.image = 图像}}}}扩展 UIImage {公共类func gif(数据:数据)->用户界面图像?{//从数据创建源守卫让源= CGImageSourceCreateWithData(数据作为CFData,零)其他{print("SwiftGif: 图片来源不存在")返回零}返回 UIImage.animatedImageWithSource(source)}公共类 func gif(url: String) ->用户界面图像?{//验证网址守卫让 bundleURL = URL(string: url) else {print("SwiftGif: 这个名为\"\(url)\" 的图片不存在")返回零}//验证数据守卫让 imageData = 试试?数据(内容:bundleURL)其他{print("SwiftGif: 无法将名为 \"\(url)\" 的图像转换为 NSData")返回零}返回 gif(数据:imageData)}公共类 func gif(name: String) ->用户界面图像?{//检查gif是否存在守卫让 bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {print("SwiftGif: 这张名为 \"\(name)\" 的图片不存在")返回零}//验证数据守卫让 imageData = 试试?数据(内容:bundleURL)其他{print("SwiftGif: 无法将名为 \"\(name)\" 的图像转换为 NSData")返回零}返回 gif(数据:imageData)}@available(iOS 9.0, *)公共类func gif(资产:字符串)->用户界面图像?{//从资产目录创建源守卫让数据资产= NSDataAsset(名称:资产)其他{print("SwiftGif: 无法将名为 \"\(asset)\" 的图像转换为 NSDataAsset")返回零}返回 gif(数据:dataAsset.data)}内部类 func delayForImageAtIndex(_ index: Int, source: CGImageSource!) ->双倍的 {无功延迟 = 0.1//获取字典让 cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)让 gifPropertiesPointer = UnsafeMutablePointer.allocate(capacity: 0)推迟 {gifPropertiesPointer.deallocate()}让 unsafePointer = Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()如果 CFDictionaryGetValueIfPresent(cfProperties, unsafePointer, gifPropertiesPointer) == false {返回延迟}让 gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)//获取延迟时间var delayObject: AnyObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),到:AnyObject.self)如果 delayObject.doubleValue == 0 {delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)}if let delayObject = delayObject as?双,delayObject >0 {延迟 = 延迟对象} 别的 {delay = 0.1//确保它们不会太快}返回延迟}内部类 func gcdForPair(_ lhs: Int?, _ rhs: Int?) ->整数{var lhs = lhsvar rhs = rhs//检查其中一个是否为零如果rhs == nil ||lhs == nil {如果rhs != nil {回归吧!} else if lhs != nil {返回 lhs!} 别的 {返回 0}}//交换模数如果 lhs!<啊!{让 ctp = lhslhs = rhsrhs = ctp}//获取最大公约数var 休息:Int而真{休息= lhs!%rs!如果休息 == 0 {回归吧!//找到了} 别的 {lhs = rhsrhs = 休息}}}内部类 func gcdForArray(_ array: [Int]) ->整数{如果 array.isEmpty {返回 1}var gcd = 数组[0]对于数组中的 val {gcd = UIImage.gcdForPair(val, gcd)}返回 gcd}内部类 func animationImageWithSource(_ source: CGImageSource) ->用户界面图像?{让计数 = CGImageSourceGetCount(source)var 图像 = [CGImage]()var 延迟 = [Int]()//填充数组对于索引在 0..

I’d like to create an animated GIF in SwiftUI.

I tried:

  1. Adding a GIF to my assets and loading that through Image("myGif")
  2. Creating a UIImage through downloading a GIF image, and passing that to Image
  3. Using this script to create a UIImage and then repeating #2.

None of the above worked, has anyone figured this out?

解决方案

You can do it by creating UIViewRepresentable from UIView. UIViewRepresentable create View for you, which you can use it in SwiftUI class.

Step1: Create UIView class for Gifview

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

Step2: Create UIViewRepresentable class for GifView

struct GIFView: UIViewRepresentable {
    var gifName: String

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

    }


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

Step3: Use that UIViewRepresentable view.

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

Note: Extension for Gif to Image. Refer: https://stackoverflow.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
    }

}

这篇关于如何创建动画 GIF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆