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

如何避免使用UIImage.draw()绘制大图时UIGraphicsImageRenderer.image()崩溃

问题核心

直接调用UIImage.draw(in:)时,系统会先将整张原图解码为完整的位图数据。以4032×3024的iPhone照片为例,解码后的内存占用约47MB(按RGBA 4字节/像素计算),再加上4000×4000画布的61MB内存,瞬间的内存峰值极易触发系统内存预警导致崩溃。

错误点分析

你的代码没有对原图做按需解码/缩放,而是直接绘制全分辨率原图——哪怕最终只需要显示其中一部分(比如photoRect区域),这会造成大量不必要的内存浪费,是崩溃的直接原因。

解决方案:按需处理原图,避免全图解码

可以先将原图缩放到photoRect的尺寸,再绘制到画布上,这样只会解码和处理需要的像素数据。同时保留UIImage类型来自动处理方向信息,无需切换到CGImage(除非你需要更底层的控制)。

修改后的代码示例:

if let loadedImage = uiImage {
    let renderer = UIGraphicsImageRenderer(size: canvasSize)
    
    let img = renderer.image { ctx in
        // 绘制白色背景
        let rectangle = CGRect(x: 0, y: 0, width: canvasSize.width, height: canvasSize.height)
        ctx.cgContext.setFillColor(UIColor.white.cgColor)
        ctx.cgContext.addRect(rectangle)
        ctx.cgContext.drawPath(using: .fill)
        
        // 先将原图缩放到photoRect尺寸,再绘制
        let scaledImage = loadedImage.scaled(to: photoRect.size)
        scaledImage.draw(in: photoRect)
    }
    
    uiImage = img
}

// 给UIImage添加扩展,实现按需缩放
extension UIImage {
    func scaled(to targetSize: CGSize) -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: targetSize)
        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: targetSize))
        }
    }
}
额外优化:适配比例+自动处理方向

如果需要保持原图的宽高比,同时让UIImage自动处理方向,可以用下面的扩展:

extension UIImage {
    func scaled(to targetSize: CGSize, preserveAspectRatio: Bool = true) -> UIImage {
        let finalSize = preserveAspectRatio ? self.aspectFitSize(for: targetSize) : targetSize
        let renderer = UIGraphicsImageRenderer(size: finalSize)
        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: finalSize))
        }
    }
    
    private func aspectFitSize(for targetSize: CGSize) -> CGSize {
        let widthRatio = targetSize.width / size.width
        let heightRatio = targetSize.height / size.height
        let scaleFactor = min(widthRatio, heightRatio)
        return CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
    }
}
关于CGImage的疑问

你不需要强制切换到CGImage,因为UIImage已经封装了方向信息,draw(in:)方法会自动应用方向变换。如果改用CGImage,你需要手动计算方向变换矩阵,反而增加复杂度,且同样需要处理解码导致的内存问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 07:12:08