iOS开发——UIImageView的contentMode、裁剪和layer属性详解

       在我们iOS开发过程中,UIImageView是一个非常常见的控件,但是我们未必会用的很溜,因为里面的有些属性不曾注意,或者很难理解。所以会对我们使用该控件带来麻烦,在布局UI过程中可能会造成意想不到的结果。这篇博客主要来讲解UIImageView中的contentMode属性以及和图片裁剪的关系,并且不得不提到就是所有UIView的重要属性:layer(CALayer)。文中提到的所有·示例代码上传至 https://github.com/chenyufeng1991/ImageWithModeCutLayer  。欢迎下载使用。

【contentMode】

      该属性是UIView所共有的属性,表示的是内容的填充模式。由于我们是在UIIMageView中该属性用的最频繁,所以我这里使用UIImageView来讲解该属性。首先演示一下我实现的效果,大家就会明白各个属性是什么作用了。


iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第1张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第2张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第3张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第4张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第5张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第6张图片


      我这里使用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会怎样:


iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第7张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第8张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第9张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第10张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第11张图片

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第12张图片


       也许,当我们把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];
}

效果如下:

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第13张图片

(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);
    }];

效果如下:

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第14张图片

显示效果和使用裁剪算法一样。所以,好好利用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;

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第15张图片.

(4)使用layer添加边框

 // 使用layer添加边框
    self.redView.layer.borderColor = [[UIColor blackColor] CGColor];
    self.redView.layer.borderWidth = 5;


iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第16张图片


(5)修改UIView的背景颜色

 // 可以通过修改layer的背景颜色从而修改UIView的背景颜色
    self.redView.layer.backgroundColor = [[UIColor yellowColor] CGColor];


iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第17张图片


(6)修改背景透明度

// 修改透明度
    self.redView.layer.opacity = 0.5;

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第18张图片

(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;

iOS开发——UIImageView的contentMode、裁剪和layer属性详解_第19张图片

(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。


你可能感兴趣的:(CAlayer,UIImageView)