LoginSignup
1
1

More than 3 years have passed since last update.

UIImageからGIFを生成する

Last updated at Posted at 2020-06-04

個人開発アプリTweetFly
複数枚の画像をGIFに変換する機能がありまして、ちょっと調べてまとめました。

下のGIFはString -> UIImage -> GIFに変換したサンプルです。
sample_typewritter.gif


import UIKit
import ImageIO
import MobileCoreServices

class GIFCreator {

    struct GIFFrame {
        var image: UIImage
        var duration: Float
    }

    static func create(with frames: [GIFFrame], resizeTo size: CGSize? = nil, filename: String = "temp.gif", completion: @escaping (URL?) -> Void) {
        DispatchQueue.global(qos: .userInteractive).async {

            // Resize images if needed
            let resizedFrames: [GIFFrame]
            if let size = size {
                let format = UIGraphicsImageRendererFormat()
                format.scale = 1
                resizedFrames = frames.map { f in
                    let resizedImage = UIGraphicsImageRenderer(size: size, format: format).image { _ in
                        f.image.draw(in: CGRect(origin: .zero, size: size))
                    }
                    return GIFFrame(image: resizedImage, duration: f.duration)
                }
            } else {
                resizedFrames = frames
            }

            let cacheUrl = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            let url = cacheUrl.appendingPathComponent(filename)
            try? FileManager.default.removeItem(at: url)
            let cfURL = url as CFURL

            if let destination = CGImageDestinationCreateWithURL(cfURL, kUTTypeGIF, resizedFrames.count, nil) {
                let fileProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
                CGImageDestinationSetProperties(destination, fileProperties as CFDictionary?)
                for frame in resizedFrames {
                    let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: frame.duration]]
                    CGImageDestinationAddImage(destination, frame.image.cgImage!, gifProperties as CFDictionary?)
                }
                DispatchQueue.main.async {
                    CGImageDestinationFinalize(destination) ? completion(url) : completion(nil)
                }
            } else {
                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        }
    }

    static func create(with images: [UIImage], perFrameDuration: Float = 0.1, resizeTo size: CGSize? = nil, filename: String = "temp.gif", completion: @escaping (URL?) -> Void) {
        GIFCreator.create(with: images.map { GIFFrame(image: $0, duration: perFrameDuration)}, resizeTo: size, filename: filename, completion: completion)
    }
}

使い方は
1、各画像の表示時間が固定の場合は[UIImage]GIFCreatorに渡せばOKです。


let images = ...
GIFCreator.create(with: images) { url in
    guard let url = url, let data = try? Data(contentsOf: url) else { return }
    let gif = UIImage(data: data)
}

2、各画像の表示時間が個別で設定したい場合


let gifFrames: [GIFCreator.GIFFrame] = [
            GIFCreator.GIFFrame(image: UIImage(), duration: 0.1),
            GIFCreator.GIFFrame(image: UIImage(), duration: 0.2),
            GIFCreator.GIFFrame(image: UIImage(), duration: 0.3)
        ]
GIFCreator.create(with: gifFrames) { url in
    guard let url = url, let data = try? Data(contentsOf: url) else { return }
    let gif = UIImage(data: data)
}

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1