個人開発アプリTweetFly
複数枚の画像をGIFに変換する機能がありまして、ちょっと調べてまとめました。
下のGIFはString
-> UIImage
-> 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)
}