iOS中UIImage调用jpegData转Data报HEIC格式不支持错误
问题说明
使用UIImagePickerController实现系统相册选图、相机拍摄功能时,在imagePickerController代理回调方法中获取到UIImage实例后,尝试通过如下代码转换为图片Data:
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage let imageData = uiImage.jpegData(compressionQuality: 1)! // 该行执行抛出错误
执行第二行转换代码时持续抛出如下错误:
findWriterForTypeAndAlternateType:119: unsupported file format 'public.heic'
异常现象:直接通过Image(uiImage)方式更新图片视图时,视图可以正常加载显示对应图片,无法定位错误触发原因。
问题根因
- 报错核心触发逻辑:苹果从iOS 11开始默认使用HEIC/HEIF作为系统图片的高效存储格式,当你从相册/相机拿到的
UIImage关联的底层资源是HEIC格式,且存在iCloud资源未完全下载、原始HEIC元数据缺失的情况时,直接调用jpegData(compressionQuality:)方法,系统底层编码组件无法自动完成HEIC到JPEG的转码流程,就会抛出HEIC格式不支持的错误。 Image(uiImage)可以正常显示的原因:SwiftUI的Image组件渲染时仅使用UIKit层已经解码完成的位图像素数据,不需要校验原始图片的完整格式元数据,也不需要对原始资源做二次编码,因此不会触发该报错。- 代码里的强制解包
!会在转码失败时直接触发应用崩溃,属于高风险写法。
可行解决方案
方案1:重绘图片后转码(兼容性最高,推荐优先使用)
拿到原始UIImage后先重新绘制生成新的位图实例,抹除原始HEIC格式的元数据关联,再执行JPEG转码,可覆盖所有选图、拍照场景:
// 基于原始UIImage尺寸创建渲染器 let renderer = UIGraphicsImageRenderer(size: uiImage.size) // 重绘生成不带原始元数据的新UIImage实例 let redrawnImage = renderer.image { _ in uiImage.draw(in: CGRect(origin: .zero, size: uiImage.size)) } // 转JPEG数据,避免强制解包,做好失败兜底 guard let imageData = redrawnImage.jpegData(compressionQuality: 0.8) else { // 转码失败时的业务兜底逻辑,比如提示用户图片处理失败 return }
方案2:配置图片选择器从源头输出JPEG格式资源
初始化UIImagePickerController时,直接指定导出的媒体类型为JPEG,避免拿到HEIC格式资源:
import UniformTypeIdentifiers let picker = UIImagePickerController() // 配置选择器仅返回JPEG格式媒体 picker.mediaTypes = [UTType.jpeg.identifier] // 相机拍摄场景额外配置,强制拍摄输出JPEG if picker.sourceType == .camera { picker.cameraCaptureMode = .photo if #available(iOS 11.0, *) { picker.videoExportPreset = AVAssetExportPresetPassthrough } }
注意:该方案需要引入UniformTypeIdentifiers、AVFoundation框架,低于iOS 14的版本需要做UTType的适配判断。
方案3:直接读取原始资源Data做处理
跳过originalImage转UIImage再转Data的流程,直接从代理回调的info字典中读取原始图片URL,加载原始Data后自行处理格式:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { picker.dismiss(animated: true) // 优先读取相册资源的原始URL if let imageURL = info[.imageURL] as? URL, let originalData = try? Data(contentsOf: imageURL) { // 此处拿到原始图片Data,可自行判断格式后按需转码 // 若需要转JPEG,可结合方案1的重绘逻辑处理 return } // 相机拍摄无本地URL的场景,再走originalImage重绘转码逻辑 }
内容的提问来源于stack exchange,提问作者KANAYO AUGUSTIN UG
相关产品推荐
相关产品推荐

