从UIImagePickerController选取的UIImage的cgImage始终为nil的问题求助
问题分析与解决方案
核心问题原因
从UIImagePickerController获取的图片如果是HEIC格式(iOS相册默认存储格式),UIImage内部实际存储的是CIImage而非CGImage,直接访问cgImage属性自然会返回nil。另外你的代码还有两个隐藏问题:
- 函数名拼写错误:
makePuzze应该是makePuzzle,会导致分割逻辑无法被正确调用 - 裁剪坐标系不匹配:
UIImage的size是考虑了图片方向的,而CGImage的坐标系以左下角为原点,直接用UIImage.size计算的矩形裁剪会出现位置偏移
修复方案
方案1:统一将UIImage转换为CGImage
不管图片是基于CIImage还是CGImage,先转成可用的CGImage实例再进行裁剪,同时修复坐标系和错误处理:
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate { @IBOutlet weak var selectButton: UIButton! let picker = UIImagePickerController() var image: UIImage! var images: [UIImage]! override func viewDidLoad() { super.viewDidLoad() picker.delegate = self picker.sourceType = .photoLibrary } @objc func selectPressed(_ sender: UIButton) { present(picker, animated: true) } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let image = info[.originalImage] as? UIImage else { picker.dismiss(animated: true) return } self.image = image // 延迟到弹窗消失后执行分割逻辑 picker.dismiss(animated: true) { [weak self] in self?.makePuzzle() } } // 修正拼写错误 func makePuzzle() { guard let splitImages = image.split(times: 5) else { print("图片分割失败") return } self.images = splitImages // 这里可以添加分割后图片的处理逻辑 } } extension UIImage { func split(times: Int) -> [UIImage]? { // 优先取cgImage,取不到则将CIImage转成CGImage guard let targetCGImage = self.cgImage ?? self.convertToCGImage() else { return nil } // 使用CGImage的原始尺寸计算分割块大小(不受UIImage方向影响) let totalWidth = targetCGImage.width let totalHeight = targetCGImage.height let pieceWidth = totalWidth / times let pieceHeight = totalHeight / times var splitImages: [UIImage] = [] // 按CGImage坐标系(左下角为原点)遍历分割 for y in 0..<times { for x in 0..<times { let cropRect = CGRect(x: x * pieceWidth, y: y * pieceHeight, width: pieceWidth, height: pieceHeight) guard let croppedCGImage = targetCGImage.cropping(to: cropRect) else { continue } // 保留原图片的缩放比例和方向 let croppedImage = UIImage(cgImage: croppedCGImage, scale: self.scale, orientation: self.imageOrientation) splitImages.append(croppedImage) } } return splitImages } // 辅助方法:将CIImage类型的UIImage转换为CGImage private func convertToCGImage() -> CGImage? { guard let ciImage = self.ciImage else { return nil } let context = CIContext(options: [.useSoftwareRenderer: false]) return context.createCGImage(ciImage, from: ciImage.extent) } }
方案2:直接使用CIImage裁剪(适合HEIC图片)
如果图片本身是CIImage类型,可以直接用Core Image API裁剪,避免格式转换的开销:
extension UIImage { func splitWithCIImage(times: Int) -> [UIImage]? { guard let targetCIImage = self.ciImage ?? CIImage(image: self) else { return nil } let imageExtent = targetCIImage.extent let pieceWidth = imageExtent.width / CGFloat(times) let pieceHeight = imageExtent.height / CGFloat(times) var splitImages: [UIImage] = [] let context = CIContext(options: [.useSoftwareRenderer: false]) for y in 0..<times { for x in 0..<times { let cropRect = CGRect(x: imageExtent.origin.x + CGFloat(x)*pieceWidth, y: imageExtent.origin.y + CGFloat(y)*pieceHeight, width: pieceWidth, height: pieceHeight) let croppedCIImage = targetCIImage.cropped(to: cropRect) guard let croppedCGImage = context.createCGImage(croppedCIImage, from: croppedCIImage.extent) else { continue } let croppedImage = UIImage(cgImage: croppedCGImage, scale: self.scale, orientation: self.imageOrientation) splitImages.append(croppedImage) } } return splitImages } }
关键修复点说明
- 兼容CIImage转CGImage:新增
convertToCGImage方法,确保所有格式的图片都能拿到可用的CGImage实例 - 修正坐标系偏差:使用CGImage的原始尺寸计算裁剪区域,避免UIImage方向带来的位置偏移
- 移除强制解包:用
guard let替代!,避免因nil导致的崩溃,同时增加错误提示 - 修正函数名拼写:将
makePuzze改为makePuzzle,确保分割逻辑能被正确调用 - 优化执行时机:在弹窗消失的回调里执行分割逻辑,避免界面卡顿
内容的提问来源于stack exchange,提问作者Neglected Sanity
相关产品推荐
相关产品推荐

