You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从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
    }
}

关键修复点说明

  1. 兼容CIImage转CGImage:新增convertToCGImage方法,确保所有格式的图片都能拿到可用的CGImage实例
  2. 修正坐标系偏差:使用CGImage的原始尺寸计算裁剪区域,避免UIImage方向带来的位置偏移
  3. 移除强制解包:用guard let替代!,避免因nil导致的崩溃,同时增加错误提示
  4. 修正函数名拼写:将makePuzze改为makePuzzle,确保分割逻辑能被正确调用
  5. 优化执行时机:在弹窗消失的回调里执行分割逻辑,避免界面卡顿

内容的提问来源于stack exchange,提问作者Neglected Sanity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:40:49