先贴一个案例,我们做类似的图片展示的时候,都会需要对相应的图片进行裁剪以适应我们现实的大小,不然图片就会被拉伸或者压缩。

不知大家有类似需求时会怎么处理,之前我一直是获取到图片数据,然后对图片裁剪到与视图框相应的比例,然后在展示。贴上代码:
UIImage * image = (UIImage*)info[UIImagePickerControllerOriginalImage];
CGFloat scale = MIN(image.size.height / 124.0f, image.size.width / 124.0f);
UIImage *cropImage = [image cropToSize:CGSizeMake(124 * scale, 124 * scale) usingMode:NYXCropModeCenter];
我们需要显示图片时,第一时间会考虑到用 UIImageView这个类来实现,然而,在iOS中,UIView这个类本身就自带类似功能的实现,相比上一种方法感觉更简单快捷。
contents属性
CALayer 有一个属性叫做contents,这个属性的类型被定义为id,意味着它可以是任何类型的对象。在这种情况下,你可以给contents属性赋任何值,你的app仍然能够编译通过。但是,在实践中,如果你给contents赋的不是CGImage,那么你得到的图层将是空白的。
contents这个奇怪的表现是由Mac OS的历史原因造成的。它之所以被定义为id类型,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作用。如果你试图在iOS平台上将UIImage的值赋给它,只能得到一个空白的图层。一些初识Core Animation的iOS开发者可能会对这个感到困惑。
头疼的不仅仅是我们刚才提到的这个问题。事实上,你真正要赋值的类型应该是CGImageRef,它是一个指向CGImage结构的指针。UIImage有一个CGImage属性,它返回一个CGImageRef,如果你想把这个值直接赋值给CALayer的contents,那你将会得到一个编译错误。因为CGImageRef并不是一个真正的Cocoa对象,而是一个Core Foundation类型。
尽管Core Foundation类型跟Cocoa对象在运行时貌似很像(被称作toll-free bridging),他们并不是类型兼容的,不过你可以通过bridged关键字转换。如果要给图层的寄宿图赋值,你可以按照以下这个方法
layer.contents = (__bridge id)image.CGImage;
maskToBounds
默认情况下,UIView仍然会绘制超过边界的内容或是子视图,在CALayer下也是这样的。
UIView有一个叫做clipsToBounds的属性可以用来决定是否显示超出边界的内容,CALayer对应的属性叫做masksToBounds。
contentsRect
CALayer的contentsRect属性允许我们在图层边框里显示寄宿图的一个子域。这涉及到图片是如何显示和拉伸的,所以要比contentsGravity灵活多了
和bounds,frame不同,contentsRect不是按点来计算的,它使用了单位坐标,单位坐标指定在0到1之间,是一个相对值(像素和点就是绝对值)。所以他们是相对与寄宿图的尺寸的。
结论
利用以上三个layer的属性,我们就可以快速实现对示图片裁剪,贴上代码:
int width = pic.bmiddle.width;
int height = pic.bmiddle.height;
CGFloat scale = (height / width) / (imageView.height / imageView.width);
if (scale < 0.99 || isnan(scale)) {
// 宽图把左右两边裁掉
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.layer.contentsRect = CGRectMake(0, 0, 1, 1);
} else {
// 高图只保留顶部
imageView.contentMode = UIViewContentModeScaleToFill;
imageView.layer.contentsRect = CGRectMake(0, 0, 1, (float)width / height);
}
这样,我们就可以根据图片的宽高比例,进行适当裁剪,达到我们所需的显示效果。