如何避免使用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
相关产品推荐
相关产品推荐

