iOS UIImageWriteToSavedPhotosAlb
引子:
需求,把UIImage
保存图片到相册。
看似很简单的需求,踩了一个坑,记录一下。
正常步骤:
- 第一步,添加长按手势到
UIImageView
上。
func addLongPressGestureRecognizer() {
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressClick))
self.qrImageView.isUserInteractionEnabled = true
self.qrImageView.addGestureRecognizer(longPress)
}
- 第二步:添加长按事件,并使用核心API
UIImageWriteToSavedPhotosAlbum
。
@objc func longPressClick() {
let alert = UIAlertController(title: "是否将二维码保存到相册?", message: nil, preferredStyle: .alert)
let action = UIAlertAction(title: "确定", style: .default) { [weak self] (action) in
guard let `self` = self else {
return
}
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(image:didFinishSavingWithError:contextInfo:)), nil) // 保存到相册
}
let cancel = UIAlertAction(title: "取消", style: .cancel, handler: nil)
alert.addAction(action)
alert.addAction(cancel)
self.present(alert, animated: true, completion: nil)
}
- 第三步:实现一个回调方法:
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo;
@objc func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if error == nil {
UIView.makeStaticToast("保存成功")
} else {
UIView.makeStaticToast("保存失败")
}
}
遇到的坑:
- 描述:保存成功,但打开相册并未找到照片。
原因是该UIImage
是通过CIImage
转过来的,
而实际上只能识别CGImage
转过来的或者本身就是UIImage
。
- 解决方案:将
UIImage -> CIImage -> CGImage -> UIImage
。
let cgImage = CIContext().createCGImage(image.ciImage!, from: image.ciImage!.extent)
let uiImage = UIImage.init(cgImage: cgImage!)
UIImageWriteToSavedPhotosAlbum(uiImage, self, #selector(self.image(image:didFinishSavingWithError:contextInfo:)), nil) // 保存到相册
问题的本质:
UIImage、CGImage、CIImage的本质区别是什么?
- CGImage:
A bitmap image or image mask.
基于像素的矩阵,每个点都对应了图片中点的像素信息。
A CGImage can only represent bitmaps. Operations in CoreGraphics, such as blend modes and masking require CGImageRefs. If you need to access and change the actual bitmap data, you can use CGImage. It can also be converted to NSBitmapImageReps.
用来描述bitmaps,如果需要访问和更改实际的位图数据,可以使用CGImage。
- CIImage:
A representation of an image to be processed or produced by Core Image filters.
A CIImage is an immutable object that represents an image. It is not an image. It only has the image data associated with it. It has all the information necessary to produce an image. You typically use CIImage objects in conjunction with other Core Image classes such as CIFilter, CIContext, CIColor, and CIVector. You can create CIImage objects with data supplied from variety of sources such as Quartz 2D images, Core Videos image, etc. It is required to use the various GPU optimized Core Image filters. They can also be converted to NSBitmapImageReps. It can be based on the CPU or the GPU.
对象也是线程安全的,它并不是一张图片。
它包含了所有生成一张图片所有的必要信息。
常用在CIFilter, CIContext, CIColor, CIVector。跟GPU的处理相关。(如滤镜)
- UIImage:
An object that manages image data in your app.
UIImage object is a high-level way to display image data. You can create images from files, from Quartz image objects, or from raw image data you receive. They are immutable and must specify an image’s properties at initialization time. This also means that these image objects are safe to use from any thread. Typically you can take NSData object containing a PNG or JPEG representation image and convert it to a UIImage.
UIImage对象是显示图像数据的高级方法。您可以从文件、Quartz图像对象或接收的原始图像数据创建图像。它们是不可变的,必须在初始化时指定图像的属性。这也意味着这些图像对象可以安全地从任何线程中使用。通常你能取得NSData对象包含一个PNG或JPEG表现图像并且转换它到一个UIImage。
主要区别:
1. 使用imageWithCGImage
生成的UIImage
:
- 重新生成
UIImage
,会把生成它的CGImageRef
保存下来 image.CIImage = nil
2. 使用imageWithCIImage
生成的UIImage
:
- 重新生成
UIImage
,会把生成它的CIImage
保存下来 image.CGImage = nil
\ | imageWithCGImage | imageWithCIImage |
---|---|---|
1 | 重新生成UIImage, 会把生成它的CGImageRef保存下来 |
重新生成UIImage, 会把生成它的CIImage保存下来 |
2 | image.CIImage = nil | image.CGImage = nil |
猜测UIImageWriteToSavedPhotosAlbum
方法底层会使用image.cgImage
,转换成照片,再保存照片到相册。
因此,不能直接使用CIImage
转换成的UIImage
,因为CIImage
转成的image.cgImage = nil
。
所以,最终解决方案是:UIImage -> CIImage -> CGImage -> UIImage
。