iOS UIImageWriteToSavedPhotosAlbum保存相冊失敗【解決方案】

引子:
需求,把UIImage保存圖片到相冊。
看似很簡單的需求,踩了一個坑,記錄一下。

正常步驟:

  • 第一步,添加長按手勢到UIImageView上。
func addLongPressGestureRecognizer() {
     let longPress = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressClick))
     self.qrImageView.isUserInteractionEnabled = true
     self.qrImageView.addGestureRecognizer(longPress)
}
  • 第二步:添加長按事件,并使用核心APIUIImageWriteToSavedPhotosAlbum
@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

PS:關于CGImage與CIImage的詳細資料

\ 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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容