UIImage 裁切图片

项目里通常会有这样一种需求,将图片按指定尺寸裁切(一般为矩形图片裁切,保留中间部分为正方形,多用于上传缩略图),如下所示:
裁切前:


裁切后:

查阅文档,我们发现系统提供了以下两个API方便我们实现:

1
2
3
Creates a bitmap image using the data contained within a subregion of an existing bitmap image.

CGImageCreateWithImageInRect(CGImageRef image, CGRect rect);

1
2
3
Creates and returns an image object with the specified scale and orientation factors:

+ (UIImage *)imageWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation;

通过这两个API,我们可以通过UIImage分类方法的形式,指定裁切范围,对图片进行裁切:

1
2
3
4
5
6
7
8
9
10
11
12
- (UIImage *)imageByCropToRect:(CGRect)rect
{
rect.origin.x *= self.scale; // Point -> Px
rect.origin.y *= self.scale;
rect.size.width *= self.scale;
rect.size.height *= self.scale;
if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
CGImageRelease(imageRef);
return image;
}

以上方法裁切后的图片是以图片像素(Pixel)为单位,有时候我们可能只想获取以点(Dot)为单位的图片,可以对方法进行一些改造:

1
2
3
4
- (UIImage *)imageByCropToRect:(CGRect)rect
{
return [self imageByCropToRect:rect scale:YES];
}

根据裁切范围和是否按屏幕分辨比放大(以像素或点为单位),对图片进行裁切:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (UIImage *)imageByCropToRect:(CGRect)rect scale:(BOOL)scale
{
CGFloat scaleFactor = 1.f;
if (scale)
{
scaleFactor = self.scale;
rect.origin.x *= scaleFactor; // Point -> Px
rect.origin.y *= scaleFactor;
rect.size.width *= scaleFactor;
rect.size.height *= scaleFactor;
}
if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:scaleFactor orientation:self.imageOrientation];
CGImageRelease(imageRef);
return image;
}

至此,方法接口主体完成。考虑到业务扩展,我们还可以在接口设计上,提供一些扩展方法供外部调用:
预先提供一些业务可能存在的裁切需求:

1
2
3
4
5
6
7
typedef NS_ENUM(NSInteger, UIImageCropStyle) {  // 裁切类型
UIImageCropStyleLeft = 0, // 左半部分
UIImageCropStyleRight, // 右半部分
UIImageCropStyleCenter, // 中间部分
UIImageCropStyleTop, // 上半部分
UIImageCropStyleBottom, // 下半部分
};

根据图片裁切类型,裁切图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- (UIImage *)imageByCropStyle:(UIImageCropStyle)style
{
CGFloat cropX = 0, cropY = 0, cropWidth = self.size.width, cropHeight = self.size.height;

if (style == UIImageCropStyleLeft)
{
cropWidth /= 2;
}
else if (style == UIImageCropStyleRight)
{
cropWidth /= 2;
cropX = cropWidth;
}
else if (style == UIImageCropStyleCenter)
{
if (cropWidth > cropHeight)
{
cropX = (cropWidth - cropHeight)/2;
cropWidth = cropHeight;
}
else if (cropWidth < cropHeight)
{
cropY = (cropHeight - cropWidth)/2;
cropHeight = cropWidth;
}
}
else if (style == UIImageCropStyleTop)
{
cropHeight /= 2;
}
else if (style == UIImageCropStyleBottom)
{
cropHeight /= 2;
cropY = cropHeight;
}

return [self imageByCropToRect:CGRectMake(cropX, cropY, cropWidth, cropHeight)];
}

将矩形图片裁切为正方形图片(保留中间部分):

1
2
3
4
- (UIImage *)imageByCropToSquare
{
return [self imageByCropStyle:UIImageCropStyleCenter];
}

以上方法满足一般的图片裁切需求,若有特殊的图片裁切需求(如将图片裁切为圆形或不规则图形),可以选择使用UIBezierPath重绘实现。

附,示例代码:https://github.com/ColinHwang/iOS-CropImage

参考资料:

[1]刚刚在线.截取UIImage指定大小区域[EB/OL].http://www.superqq.com/blog/2015/07/26/jie-qu-uiimagezhi-ding-da-xiao-qu-yu ,2015-7-26.
[2]Apple Inc.UIImage Reference Class[EB/OL].https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/index.html ,2016-5-30.