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