先貼一個案例,我們做類似的圖片展示的時候,都會需要對相應的圖片進行裁剪以適應我們現實的大小,不然圖片就會被拉伸或者壓縮。
不知大家有類似需求時會怎么處理,之前我一直是獲取到圖片數據,然后對圖片裁剪到與視圖框相應的比例,然后在展示。貼上代碼:
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);
}
這樣,我們就可以根據圖片的寬高比例,進行適當裁剪,達到我們所需的顯示效果。