SwiftUI

SwiftUI 中显示gif图片

有时候我们需要在View中显示Gif图片,需要借助GifImageViewController

  • 把图片拷贝到工程
  • 在SwiftUI中 调用GifImageView即可。
struct LoadingView: View {
    @State var gifName: String = "Loading"
    var body: some View {
        VStack(alignment: .center, spacing: 0){  
            GifImageView(imageName: self.$gifName)
  • GifImageView代码 注意修改gifImageWithName 图片路径。
struct GifImageView: UIViewControllerRepresentable{
    @Binding var imageName: String

    func makeUIViewController(context: Context) -> GifImageViewController {
        let picker = GifImageViewController(imageName: self.imageName)

        return picker
    }
    func updateUIViewController(_ uiViewController: GifImageViewController,context: UIViewControllerRepresentableContext<GifImageView>) {
        uiViewController.imageView.removeFromSuperview()
        uiViewController.imageName = imageName
        uiViewController.viewDidLoad()
    }

}
class GifImageViewController: UIViewController {
//    lazy var imageView: UIImageView = {
    var imageView: UIImageView = UIImageView()
//            return imageView
//        }()
    fileprivate var imageName: String = ""
    convenience init(imageName: String) {
        self.init()
        self.imageName = imageName
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        
        /************************ Load GIF image Using Name ********************/
//        print(self.imageName)
        let jeremyGif = UIImage.gifImageWithName(self.imageName)
        imageView = UIImageView(image: jeremyGif)
//        imageView.frame = self.view.bounds
        imageView.frame = CGRect(x: 0, y: 0, width: 42, height: 42)
        view.addSubview(imageView)
        
        /************************ 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)
//
//        /************************ Load GIF image 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)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

extension UIImage {
    
    public class func gifImageWithData(_ data: Data) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
            print("image doesn't exist")
            return nil
        }
        
        return UIImage.animatedImageWithSource(source)
    }
    
    public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
        guard let bundleURL:URL = URL(string: gifUrl)
            else {
                print("image named \"\(gifUrl)\" doesn't exist")
                return nil
        }
        guard let imageData = try? Data(contentsOf: bundleURL) else {
            print("image named \"\(gifUrl)\" into NSData")
            return nil
        }
        
        return gifImageWithData(imageData)
    }
    
    // ※subdirectory需要修改
    public class func gifImageWithName(_ name: String) -> UIImage? {
        guard let bundleURL = Bundle.main
            .url(forResource: name, withExtension: "gif", subdirectory: "Music") else {
                print("SwiftGif: This image named \"\(name)\" does not exist")
                return nil
        }
        guard let imageData = try? Data(contentsOf: bundleURL) else {
            print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
            return nil
        }
        
        return gifImageWithData(imageData)
    }
    
    class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
        var delay = 0.03
        
        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.03 {
            delay = 0.03
        }
        
        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(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(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(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
    }
}