在我们iOS开发过程中,UIImageView是一个非常常见的控件,但是我们未必会用的很溜,因为里面的有些属性不曾注意,或者很难理解。所以会对我们使用该控件带来麻烦,在布局UI过程中可能会造成意想不到的结果。这篇博客主要来讲解UIImageView中的contentMode属性以及和图片裁剪的关系,并且不得不提到就是所有UIView的重要属性:layer(CALayer)。文中提到的所有·示例代码上传至 https://github.com/chenyufeng1991/ImageWithModeCutLayer 。欢迎下载使用。
【contentMode】
该属性是UIView所共有的属性,表示的是内容的填充模式。由于我们是在UIIMageView中该属性用的最频繁,所以我这里使用UIImageView来讲解该属性。首先演示一下我实现的效果,大家就会明白各个属性是什么作用了。
。
我这里使用CollectionView把contentMode共13种填充模式都显示了出来,cell的背景颜色是黄色,也就是说图片没有覆盖的区域显示的是cell默认的黄色背景。这样显示应该比较直观。下面对13种模式描述一下:
(1)ScaleToFill:这是图片显示的默认模式。图片进行非等比例缩放,直到填铺满整个View区域。所以往往造成图片的变形。也就是图片的长度上缩放一定的比例填满显示区域,在宽度上缩放一定的比例填满显示区域。
(2)ScaleAspectFit:这是等比例缩放,所以使用这种缩放模式的图片永远不会变形。图片按一定比例缩放,直到在长度上或者宽度上达到View的边界就停止。没有填满区域就显示View的背景。
(3)ScaleAspectFill:这也是等比例缩放,图片也不会变形。这种缩放和上面的ScaleAspectFit正好相反,图片按一定比例缩放,直到最短的边达到View的边界。所以这种缩放一定会铺满View,超出View的图片你可以选择截掉或者不截掉。
(4)Redraw:重绘。说实话也不清楚这种模式的特点,仅仅实现效果和下面要讲的Left是一样的。
(5)Center:等比缩放,居中显示。
(6)Top:等比缩放,顶部对齐显示。
(7)Bottom:等比缩放,底部对齐显示。
(8)Left:等比缩放,左侧对齐显示。
(9)Right:等比缩放,右侧对齐显示。
(10)TopLeft:等比缩放,左上角对齐显示。
(11)TopRight:等比缩放,右上角对齐显示。
(12)BottomLeft:等比缩放,左下角对齐显示。
(13)BottomRight:等比缩放,右下角对齐显示。
我这里原始图片的大小为:380*140. UIImageView的大小为180*180. 还有非常重要的一点就是我设置了UIImageView的属性ImageView.layer.masksToBounds = YES. 这个属性
是可以把超过View区域部分截掉,默认是NO。我们来看看在上面的案例如果masksToBounds = NO会怎样:
也许,当我们把masksToBounds 设置为NO,这样contentMode才更加容易理解。这个属性的作用也就是是否把超过区域部分截掉。所以在这里我们可以知道,并不是我们设置View多大,内容就一定显示在该区域内,还和它的显示模式有关。
【contentMode和图片裁剪】
通过我上面的案例,其实可以发现contentMode也有图片裁剪的功能,那么和普通的图片裁剪方法有什么差异呢,我们简单来比较一下:
(1)正常的图片显示:
// 原始的显示图片,默认的contentMode为ContentModeScaleToFill self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; [self.imageView setImage:[UIImage imageNamed:@"ford"]]; self.imageView.backgroundColor = [UIColor yellowColor]; [self.view addSubview:self.imageView]; [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.view); make.width.equalTo(@100); make.height.equalTo(@100); }];
效果如下:
。
(2)经过裁剪:
// 经过裁剪 self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; UIImage *cutImage = [self cutImage:[UIImage imageNamed:@"ford"]]; [self.imageView setImage:cutImage]; self.imageView.backgroundColor = [UIColor yellowColor]; [self.view addSubview:self.imageView]; [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.view); make.width.equalTo(@100); make.height.equalTo(@100); }];
//裁剪图片 /** * * */ - (UIImage *)cutImage:(UIImage*)originImage { CGSize newImageSize; CGImageRef imageRef = nil; CGSize imageViewSize = self.imageView.frame.size; CGSize originImageSize = originImage.size; if ((originImageSize.width / originImageSize.height) < (imageViewSize.width / imageViewSize.height)) { // imageView的宽高比 > image的宽高比 newImageSize.width = originImageSize.width; newImageSize.height = imageViewSize.height * (originImageSize.width / imageViewSize.width); imageRef = CGImageCreateWithImageInRect([originImage CGImage], CGRectMake(0, fabs(originImageSize.height - newImageSize.height) / 2, newImageSize.width, newImageSize.height)); } else { // image的宽高比 > imageView的宽高比 : 也就是说原始图片比较狭长 newImageSize.height = originImageSize.height; newImageSize.width = imageViewSize.width * (originImageSize.height / imageViewSize.height); imageRef = CGImageCreateWithImageInRect([originImage CGImage], CGRectMake(fabs(originImageSize.width - newImageSize.width) / 2, 0, newImageSize.width, newImageSize.height)); } return [UIImage imageWithCGImage:imageRef]; }
效果如下:
。
(3)使用masksToBounds和ScaleAspectFill设置裁剪效果:
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; [self.imageView setImage:[UIImage imageNamed:@"ford"]]; self.imageView.backgroundColor = [UIColor yellowColor]; self.imageView.contentMode = UIViewContentModeScaleAspectFill; self.imageView.layer.masksToBounds = YES; [self.view addSubview:self.imageView]; [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.view); make.width.equalTo(@100); make.height.equalTo(@100); }];
效果如下:
。
显示效果和使用裁剪算法一样。所以,好好利用contentMode和其他属性,可以简化我们的代码。
【layer属性的简单介绍】
任何一个UIView都有一个layer属性,是属于CALayer类型。这个东西一般初学者很少去注意,但是却能发挥大作用。这里还是通过UIImageView来进行演示,图片还是我上面用到的380*140的“福特”图片。
(1)声明属性:
@property (nonatomic, strong) UIImageView *redView; @property (nonatomic, strong) UIImage *fordImage;
(2)初始化UIView
self.redView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 50, 200, 200)]; self.redView.backgroundColor = [UIColor redColor]; [self.view addSubview:self.redView]; [self.redView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.view); make.width.equalTo(@100); make.height.equalTo(@100); }];
(3)使用layer设置圆角
// 使用layer属性设置圆角 self.redView.layer.cornerRadius = 10;
.
(4)使用layer添加边框
// 使用layer添加边框 self.redView.layer.borderColor = [[UIColor blackColor] CGColor]; self.redView.layer.borderWidth = 5;
。
(5)修改UIView的背景颜色
// 可以通过修改layer的背景颜色从而修改UIView的背景颜色 self.redView.layer.backgroundColor = [[UIColor yellowColor] CGColor];
。
(6)修改背景透明度
// 修改透明度 self.redView.layer.opacity = 0.5;
。
(7)设置View的阴影
// 设置View的阴影 self.redView.layer.shadowColor = [[UIColor blueColor] CGColor]; self.redView.layer.shadowOffset = CGSizeMake(10, 10); self.redView.layer.shadowOpacity = 1.0; // 一定要设置阴影的透明度,因为默认为0 self.redView.layer.shadowRadius = 10;
。
(8)masksToBounds与截取
// 设置超过layer区域部分是否截掉 self.redView.layer.masksToBounds = NO; /** * 在ImageView中加入一张图片 self.redView.layer.masksToBounds = YES;时可以把超出layer部分截掉。 */ self.fordImage = [UIImage imageNamed:@"ford"]; self.redView.contentMode = UIViewContentModeScaleAspectFill; self.redView.image = self.fordImage;
。
(9)隐藏layer,同时也隐藏View
// hidden设置为YES会隐藏Layer,视图不可见 self.redView.layer.hidden = YES;
(10)子Layer,父Layer
// 由于self.view是redView的父视图,所以self.view.layer同样也是redView.layer的父图层 CALayer *superLayer = self.redView.layer.superlayer; CALayer *selfViewLayer = self.view.layer; NSLog(@"superLayer = %@,selfViewLayer = %@",superLayer,selfViewLayer);
父子View的层级结构同样适用于父子Layer的层级结构。
(11)打印Layer的各项参数
CALayer *redLayer = self.redView.layer; NSLog(@"redView.bounds = %@,redLayer.bounds = %@",NSStringFromCGRect(self.redView.bounds),NSStringFromCGRect(redLayer.bounds)); NSLog(@"redView.center = %@,redLayer.position = %@",NSStringFromCGPoint(self.redView.center),NSStringFromCGPoint(redLayer.position)); NSLog(@"redLayer.zPosition = %f",redLayer.zPosition); NSLog(@"redLayer.anchorPoint = %@",NSStringFromCGPoint(redLayer.anchorPoint)); NSLog(@"redView.frame = %@,redLayer.frame = %@",NSStringFromCGRect(self.redView.frame),NSStringFromCGRect(redLayer.frame)); NSLog(@"fordImage.size = %@",NSStringFromCGSize(self.fordImage.size)); // 图片的原始大小
view中的很多属性基本是和layer中吻合的。View中的bounds等同于Layer中的bounds;View中的center等同于layer中的center;View中的frame等同于layer中的frame。
(12)对View和Layer的总结
-- 每个UIView都有CALayer,即UIView.layer;
-- CALayer能够对UIView做许多的设置,如阴影,边框,圆角,和透明效果;
-- CALayer重要属性
shadowPath:设置CALayer阴影(shadow)的位置;
shadowOffset:shadow在X,Y轴上的偏移;
shadowOpacity:shadow的透明效果;
shadowRadius:shadow的圆角;
masksToBounds:防止子元素大小溢出父元素,如果要防止溢出,设置为YES;
borderWidth和borderColor:边框颜色和宽度;
bounds:设置UIView大小;
opacity:UIView的透明效果;
cornerRadius:UIView的圆角;
-- UIView可以响应事件,CALayer不可以
UIView继承自UIResponder,在UIResponder中定义了处理各种事件和事件传递的接口。而CALayer直接继承NSObject,并没有相应的处理事件的接口。
-- 一个CALayer的frame是由它的anchorPoint,position,bounds和transform共同决定,而一个View的frame只是简单的返回Layer的frame,同样View的center和bounds也是返回layer的一些属性。
-- UIView主要是对显示内容的管理,而CALayer主要侧重显示内容的绘制。UIView是CALayer的CALayerDelegate。
-- 每个UIView内部都有一个CALayer在背后提供内容的绘制和显示,并且UIView的尺寸样式都由内部的Layer提供。两者都有树状层级结构,layer内部有SubLayers, View内部有SubViews。
-- 两者最明显的区别是View可以接受并处理事件,而Layer不可以。View是Layer的代理Delegate。