Swift4+iOS11开发:UIImage无法保存至iPhone相册问题
Hey there! Let's troubleshoot why your image isn't saving to the iPhone photo album. I've spotted a few key issues that are likely causing this problem—let's break them down and fix them step by step:
1. Missing Photo Library Access Permission
First off, iOS requires explicit user permission to save media to the photo album. If you haven't added the required description keys to your Info.plist, the save operation will silently fail (or throw an error, depending on the iOS version).
Add these two keys to your Info.plist (including both ensures compatibility across older and newer iOS versions):
NSPhotoLibraryAddUsageDescription: A clear explanation of why you need access (e.g., "We need permission to save your captured images to your photo album.")NSPhotoLibraryUsageDescription: Older iOS versions rely on this key, so it's safe to include it as well.
To add these, right-click your Info.plist → select "Open As" → "Source Code", then paste these lines inside the <dict> tag:
<key>NSPhotoLibraryUsageDescription</key> <string>We need permission to save your captured images to your photo album.</string> <key>NSPhotoLibraryAddUsageDescription</key> <string>We need permission to save your captured images to your photo album.</string>
2. CIImage-Based UIImage Has No Backing CGImage
Your current code creates a UIImage directly from a CIImage using UIImage(ciImage: ciImage...). The problem here is that this type of UIImage doesn't have a concrete CGImage backing it up—and UIImageWriteToSavedPhotosAlbum requires a valid pixel representation to save successfully.
Let's fix your CMSampleBuffer extension to render the CIImage into a proper CGImage first:
extension CMSampleBuffer { func image(orientation: UIImageOrientation = .up, scale: CGFloat = 1.0) -> UIImage? { guard let buffer = CMSampleBufferGetImageBuffer(self) else { return nil } let ciImage = CIImage(cvPixelBuffer: buffer) // Render CIImage to CGImage to ensure a valid pixel representation let context = CIContext(options: [.useSoftwareRenderer: false]) guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else { return nil } return UIImage(cgImage: cgImage, scale: scale, orientation: orientation) } }
3. Add Safety Checks & Ensure Main Thread Execution
It's smart to add safeguards for nil images and explicitly run the save operation on the main thread (since photo library interactions and UI alerts should always happen on the main thread).
Update your saveButton action in PreviewViewController:
@IBAction func saveButton(_ sender: Any) { guard let imageToSave = imageView.image else { let ac = UIAlertController(title: "Oops!", message: "No image available to save.", preferredStyle: .alert) ac.addAction(UIAlertAction(title: "OK", style: .default)) present(ac, animated: true) return } // Dispatch to main thread for safe photo library access DispatchQueue.main.async { UIImageWriteToSavedPhotosAlbum(imageToSave, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil) } }
Bonus: Add a Check for Nil Image in ViewController
To avoid passing a nil image to PreviewViewController, add a safety check in your prepare(for:sender:) method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showPhotoSegue" { guard let capturedImage = videoCapture.image else { let ac = UIAlertController(title: "Error", message: "No image was captured.", preferredStyle: .alert) ac.addAction(UIAlertAction(title: "OK", style: .default)) present(ac, animated: true) return } let previewVC = segue.destination as! PreviewViewController previewVC.image = capturedImage } }
After making these changes, your image should save to the photo album without issues. If you run into any other snags, feel free to follow up!
内容的提问来源于stack exchange,提问作者davidchai21

