如何根据CGRect从UIImageView中提取子UIImage?
从UIImageView中提取指定区域的原图子图(适配任意contentMode)
问题场景
给定一个UIImageView,以及该视图内部的一个CGRect,需要提取该区域对应的原图子图。当图片与UIImageView尺寸匹配时,用CGImageCreateWithImageInRect()就能实现,但二者尺寸不匹配、且contentMode为任意内置值时,常规方法无法得到正确结果,使用renderInContext:也会因为视图渲染特性(比如包含背景、边框等)导致结果不符合预期。
解决方案
核心思路是:先计算原图在UIImageView中的实际显示区域与变换关系,将视图坐标系下的目标CGRect转换为原图坐标系下的对应区域,再从原图中提取子图。
代码实现(UIImageView分类)
#import <UIKit/UIKit.h> @interface UIImageView (ImageExtraction) - (UIImage *)extractedOriginalImageFromViewRect:(CGRect)viewRect; @end @implementation UIImageView (ImageExtraction) - (UIImage *)extractedOriginalImageFromViewRect:(CGRect)viewRect { if (!self.image) return nil; // 1. 获取原图尺寸和视图尺寸 CGSize imageSize = self.image.size; CGSize viewSize = self.bounds.size; // 2. 计算原图在视图中的实际显示区域(适配所有contentMode) CGRect imageDisplayRect = AVMakeRectWithAspectRatioInsideRect(imageSize, self.bounds); // 3. 计算缩放比例:原图尺寸 / 显示区域尺寸 CGFloat scaleX = imageSize.width / imageDisplayRect.size.width; CGFloat scaleY = imageSize.height / imageDisplayRect.size.height; // 4. 将视图坐标系的Rect转换为原图坐标系的Rect // 先把viewRect转换为相对于imageDisplayRect的坐标 CGRect relativeRect = CGRectMake( viewRect.origin.x - imageDisplayRect.origin.x, viewRect.origin.y - imageDisplayRect.origin.y, viewRect.size.width, viewRect.size.height ); // 再缩放得到原图中的Rect CGRect imageRect = CGRectMake( relativeRect.origin.x * scaleX, relativeRect.origin.y * scaleY, relativeRect.size.width * scaleX, relativeRect.size.height * scaleY ); // 5. 修正CGImage坐标系(Y轴与UIKit相反) CGFloat correctedY = imageSize.height - imageRect.origin.y - imageRect.size.height; CGRect correctedImageRect = CGRectMake(imageRect.origin.x, correctedY, imageRect.size.width, imageRect.size.height); // 6. 提取子图 CGImageRef subImageRef = CGImageCreateWithImageInRect(self.image.CGImage, correctedImageRect); UIImage *subImage = [UIImage imageWithCGImage:subImageRef scale:self.image.scale orientation:self.image.imageOrientation]; CGImageRelease(subImageRef); return subImage; } @end
关键步骤说明
- 计算显示区域:
AVMakeRectWithAspectRatioInsideRect会根据contentMode自动计算原图在视图中的实际显示位置和尺寸,不管是ScaleAspectFit、ScaleAspectFill还是其他对齐模式,都能准确得到显示区域。 - 坐标转换:先把视图中的目标Rect转换成相对于显示区域的相对坐标,再通过缩放比例映射到原图坐标系,确保提取的是原图对应位置的像素。
- 坐标系修正:
CGImage的Y轴原点在左下角,而UIKit的Y轴原点在左上角,所以需要反转Y轴坐标才能得到正确的区域。 - 适配Retina屏:最终生成的
UIImage保留了原图的scale和imageOrientation,保证子图的清晰度和方向正确。
使用示例
// 假设imageView是目标UIImageView,targetRect是视图内的目标区域 UIImage *extractedImage = [imageView extractedOriginalImageFromViewRect:targetRect];
内容的提问来源于stack exchange,提问作者DescendantOfLoto
相关产品推荐
相关产品推荐

