iOS開發——UIImageView的contentMode、裁剪和layer屬性詳解 – iPhone手機開發 iPhone軟體開發教學課程

在我們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。

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *