You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI从相册选GIF:保留动画、获取时长及完整上传问题

SwiftUI下GIF处理解决方案

1. 显示带动画的GIF

SwiftUI原生Image不支持动画GIF,需通过UIViewRepresentable包装UIKit的UIImageView实现:

struct AnimatedGIFView: UIViewRepresentable {
    let data: Data
    
    func makeUIView(context: Context) -> UIImageView {
        let imageView = UIImageView()
        if let gifImage = UIImage.gif(data: data) {
            imageView.image = gifImage
            imageView.contentMode = .scaleAspectFit
            imageView.startAnimating()
        }
        return imageView
    }
    
    func updateUIView(_ uiView: UIImageView, context: Context) {}
}

extension UIImage {
    static func gif(data: Data) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
            return nil
        }
        let count = CGImageSourceGetCount(source)
        var images = [UIImage]()
        var delays = [Double]()
        
        for i in 0..<count {
            guard let cgImage = CGImageSourceCreateImageAtIndex(source, i, nil) else { continue }
            images.append(UIImage(cgImage: cgImage))
            
            guard let properties = CGImageSourceCopyPropertiesAtIndex(source, i, nil) as? [String: Any],
                  let gifProperties = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any],
                  let delayTime = gifProperties[kCGImagePropertyGIFDelayTime as String] as? Double else {
                delays.append(0.1)
                continue
            }
            delays.append(delayTime)
        }
        return UIImage.animatedImage(with: images, duration: delays.reduce(0, +))
    }
}

使用示例:

// 假设已获取GIF原始data
AnimatedGIFView(data: gifData)
    .frame(width: 200, height: 200)

2. 获取GIF总时长

封装解析函数直接从原始数据计算时长:

func getGIFDuration(from data: Data) -> Double {
    guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
        return 0
    }
    let count = CGImageSourceGetCount(source)
    var totalDuration = 0.0
    
    for i in 0..<count {
        guard let properties = CGImageSourceCopyPropertiesAtIndex(source, i, nil) as? [String: Any],
              let gifProperties = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any],
              let delayTime = gifProperties[kCGImagePropertyGIFDelayTime as String] as? Double else {
            totalDuration += 0.1
            continue
        }
        totalDuration += delayTime
    }
    return totalDuration
}

调用示例:

else if mimeType == "image/gif" {
    let duration = getGIFDuration(from: data)
    print("GIF时长:\(duration)秒")
    // 后续逻辑...
}

3. 解决GIF上传末尾缺失问题

问题根源是将原始GIF数据转成UIImage时丢失了格式信息,直接使用PhotosPicker获取的原始data上传即可保留完整数据:

修改你的GIF分支代码:

else if mimeType == "image/gif" {
    let gifDuration = getGIFDuration(from: data)
    print("GIF时长:\(gifDuration)")
    
    // 直接使用原始数据,跳过UIImage转换
    uploadData = data
    
    // 存储data用于AnimatedGIFView显示
    self.gifData = data
    
    do {
        try await uploadToServer(mime: mimeType)
    } catch {
        print(error)
    }
}

在视图中替换原Image(uiImage: image!):

if let gifData = gifData {
    AnimatedGIFView(data: gifData)
        .scaledToFit()
} else if let image = image {
    Image(uiImage: image)
        .resizable()
        .scaledToFit()
}

内容的提问来源于stack exchange,提问作者TheGreatCornholio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:24:52