這是一種實現 UIView
鏤空效果的方案,能夠快速實現任意形狀的鏤空、文字的鏤空、帶鏤空的毛玻璃效果等。本質上是 UIView
的 maskView
效果的「取反」。git
首先來複習一下遮罩效果的實現。若是咱們有一張圖片,又剛好有一個圓,當咱們把圓設置爲圖片的遮罩時,會獲得這樣的結果。github
代碼實現看上去像是這樣:編程
view.maskView = maskView;
複製代碼
那麼問題來了,若是咱們但願獲得下面的結果,該怎麼作呢?這看起來像是圖層的相減,即原來的圖層減去遮罩的部分。markdown
惋惜蘋果爸爸不夠貼心,沒有提供方便的接口調用。讓咱們來看看能夠怎麼實現。ide
咱們的最終目標是,封裝出一個接口,調用方式相似於 maskView
屬性,能夠很方便地對一個 UIView
作鏤空效果。oop
注: 如下用
originView
指代須要上效果的view
,用maskView
指代充當遮罩的view
。ui
目前看來,能夠從兩個方向入手:spa
maskView
自己方式一是指,在設置這個屬性的時候,對 originView
的視圖進行從新繪製,而後在繪製的時候,減掉 maskView
的區域。3d
方式二是指,當拿到 maskView
的時候,先對 maskView
自己先進行處理,將遮罩範圍取反。而後再作遮罩效果,因爲遮罩的區域已經相反,因而獲得的結果也是相反的,就達到鏤空的目的。code
看上去方式二比較靠譜,並且最後是調用 UIView
的 setMaskView:
來實現,還能夠保留原來遮罩的一些特性。好比當修改 maskView
的 frame
的時候, originView
的遮罩位置也會相應改變。
生成相反的遮罩圖能夠分爲三步。假設一開始拿到的 maskView
是下面這樣,讓咱們來看下,轉換過程當中遮罩圖每一步的變化。
注: 爲了更直觀的效果,圖片中透明的部分用灰白相間格子來表示(如下相同)。
一、將 maskView
轉化爲 UIImage
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale); CGContextTranslateCTM(UIGraphicsGetCurrentContext(), view.frame.origin.x, view.frame.origin.y); [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 複製代碼
這一步拿到了 maskView
對應的 image
圖像。此時遮罩圖的大小會被同步爲 originView
的大小。
二、將 UIImage
轉換爲只有 alpha
通道的 CGContextRef
CGImageRef originalMaskImage = [image CGImage]; float width = CGImageGetWidth(originalMaskImage); float height = CGImageGetHeight(originalMaskImage); int strideLength = ceil(width); unsigned char * alphaData = calloc(strideLength * height, sizeof(unsigned char)); CGContextRef alphaOnlyContext = CGBitmapContextCreate(alphaData, width, height, 8, strideLength, NULL, kCGImageAlphaOnly); CGContextDrawImage(alphaOnlyContext, CGRectMake(0, 0, width, height), originalMaskImage); 複製代碼
這時候的 alphaOnlyContext
對應的圖像是下面這樣,只保留了 alpha
通道。
三、將 CGContextRef
中的 alpha
值進行遍歷轉換
for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { unsigned char val = alphaData[y*strideLength + x]; val = 255 - val; alphaData[y*strideLength + x] = val; } } CGImageRef alphaMaskImage = CGBitmapContextCreateImage(alphaOnlyContext); UIImage *result = [UIImage imageWithCGImage:alphaMaskImage]; 複製代碼
轉換後,得到的 result
圖像是:
因而,咱們就能夠用 result
愉快地進行 mask
了。
咱們能夠將上述的步驟,封裝爲一個方法,用 category
來實現。
@interface UIView (MFSubtractMask) - (void)setSubtractMaskView:(UIView *)view; - (UIView *)subtractMaskView; @end 複製代碼
這樣調用起來就十分方便了,一行代碼搞定:
view.subtractMaskView = maskView;
複製代碼
subtractMaskView
不會自動刷新咱們知道,當 UIView
的 maskView
的內容動態修改時,會實時反映到 UIView
中。但在本項目中, subtractMaskView
屬性會生成一張全新的圖片來做爲遮罩圖,由於不會根據 subtractMaskView
的內容實時來刷新視圖。若是須要更新,必須手動調用 setSubtractMaskView:
方法來從新生成遮罩圖。
setSubtractMaskView:
不宜被頻繁調用setSubtractMaskView:
本質上是生成一個新的遮罩圖的過程,該過程涉及圖片像素的遍歷轉換,較爲耗時,不宜頻繁調用。
綜上所述,這種方案適合只生成一次遮罩圖的場景。
請到 GitHub 上查看完整代碼。
獲取更佳的閱讀體驗,請訪問原文地址 【Lyman's Blog】一行代碼實現 UIView 鏤空效果