UIImage에서 GIF 생성

16363 단어 iOSSwiftSwift5
개인 개발 앱 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)
}

좋은 웹페이지 즐겨찾기