我正在尝试将多个 UIImage 组合为 1 个 gif 动画图像。如果帧数 (UIImages) 超过 20,我会遇到问题。应用程序由于内存泄漏而关闭。当
createGIF
开始制作gif时,内存从200MB跳到2GB,并不断增加,直到应用程序因内存泄漏而崩溃。
这是我的代码:
func createGIF(from images: [UIImage], loopCount: Int = 0, frameDuration: TimeInterval = 0.1, completion: @escaping (URL?) -> Void) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
print("Error: Unable to create documentsDirectory.")
completion(nil)
return
}
let gifURL = documentsDirectory.appendingPathComponent("\(UUID().uuidString).gif")
guard let destination = CGImageDestinationCreateWithURL(gifURL as CFURL, kUTTypeGIF, images.count, nil) else {
print("Error: Unable to create CGImageDestination.")
completion(nil)
return
}
let gifProperties = [
kCGImagePropertyGIFDictionary as String: [
kCGImagePropertyGIFLoopCount as String: loopCount
]
]
CGImageDestinationSetProperties(destination, gifProperties as CFDictionary)
// Process frames in batches to reduce memory usage
let batchSize = 10
let totalFrames = images.count
for startIndex in stride(from: 0, to: totalFrames, by: batchSize) {
let endIndex = min(startIndex + batchSize, totalFrames)
let batchImages = Array(images[startIndex..<endIndex])
for image in batchImages {
if let cgImage = image.cgImage {
let frameProperties = [
kCGImagePropertyGIFDictionary as String: [
kCGImagePropertyGIFDelayTime as String: frameDuration
]
]
CGImageDestinationAddImage(destination, cgImage, frameProperties as CFDictionary)
} else {
print("Error: Unable to retrieve CGImage from UIImage.")
completion(nil)
return
}
}
}
guard CGImageDestinationFinalize(destination) else {
print("Error: Failed to finalize CGImageDestination.")
completion(nil)
return
}
completion(gifURL)
}
我正在尝试从 UIImage 数组实现贴纸动画 gif。 我在这里忘记了什么或者当我使用超过 20 FPS 时导致内存增加到超过 2GB 的问题是什么。
好吧,在生成实际的 gif 图像之前使用
DispatchGroup
并调整图像大小并将它们转换为 CGImage 解决了我的问题,生成 gif 时内存最大在 150 到 200mb 之间。
这是最终的代码可以帮助某人:
public func generateGifFromImages(images: [UIImage], colorSpace: colorSpace, delayTime: Double, loopCount: Int,completion: @escaping (URL?) -> Void) {
// create empty file to hold gif
let destinationFilename = String(NSUUID().uuidString + ".gif")
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(destinationFilename)
let gifGroup = DispatchGroup()
var tempImages: [UIImage] = []
for image in images {
gifGroup.enter()
tempImages.append(image.resized(to: CGSize(width: 512,height: 512)))
gifGroup.leave()
}
gifGroup.notify(queue: .main) {
let gifFileProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: loopCount]] as CFDictionary
let gifFrameProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: delayTime]] as CFDictionary
if let url = fileURL as CFURL? {
if let destination = CGImageDestinationCreateWithURL(url, UTType.gif.identifier as CFString, images.count, nil) {
CGImageDestinationSetProperties(destination, gifFileProperties)
for image in tempImages {
if let cgImage = image.cgImage {
CGImageDestinationAddImage(destination, cgImage, gifFrameProperties)
}
}
if !CGImageDestinationFinalize(destination) {
print("Failed to finalize the image destination")
}else{
completion(fileURL)
}
}
}
}
}
这是调整大小扩展:
extension UIImage {
/**
Resizes the image.
- Parameters:
- scale: If this is 1, `newSize` is the size in pixels.
*/
@nonobjc public func resized(to newSize: CGSize, scale: CGFloat = 1) -> UIImage {
let format = UIGraphicsImageRendererFormat.default()
format.scale = scale
let renderer = UIGraphicsImageRenderer(size: newSize, format: format)
let image = renderer.image { _ in
draw(in: CGRect(origin: .zero, size: newSize))
}
return image
}
}