
這是一種實現 UIView 鏤空效果的方案,可以快速實現任意形狀的鏤空、文字的鏤空、帶鏤空的毛玻璃效果等。本質上是 UIView 的 maskView 效果。
前言
首先來復習一下遮罩效果的實現。如果我們有一張圖片,又恰好有一個圓,當我們把圓設置為圖片的遮罩時,會得到這樣的結果。

代碼實現看上去像是這樣:
view.maskView = maskView;
那么問題來了,如果我們希望得到下面的結果,該怎么做呢?這看起來像是圖層的相減,即原來的圖層減去遮罩的部分。

可惜蘋果爸爸不夠貼心,沒有提供方便的接口調用。讓我們來看看可以怎么實現。
一、思路
我們的最終目標是,封裝出一個接口,調用方式類似于 maskView 屬性,可以很方便地對一個 UIView 做鏤空效果。
注:以下用 originView 指代需要上效果的 view ,用 maskView 指代充當遮罩的 view 。
目前看來,可以從兩個方向入手:
方式一是指,在設置這個屬性的時候,對 originView 的視圖進行重新繪制,然后在繪制的時候,減掉 maskView 的區域。
方式二是指,當拿到 maskView 的時候,先對 maskView 本身先進行處理,將遮罩范圍取反。然后再做遮罩效果,由于遮罩的區域已經相反,于是得到的結果也是相反的,就達到鏤空的目的。
看上去方式二比較靠譜,而且最后是調用 UIView 的 setMaskView: 來實現,還可以保留原來遮罩的一些特性。比如當修改 maskView 的 frame 的時候, originView 的遮罩位置也會相應改變。
二、實現
生成相反的遮罩圖可以分為三步。假設一開始拿到的 maskView 是下面這樣,讓我們來看下,轉換過程中遮罩圖每一步的變化。

注:為了更直觀的效果,圖片中透明的部分用灰白相間格子來表示(以下相同)。
1、將 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 的大小。
2、將
UIImage 轉換為只有 alpha 通道的 CGContextRef
CGImageRef originalMaskImage = [image CGImage];float width = CGImageGetWidth(originalMaskImage);float height = CGImageGetHeight(originalMaskImage); int strideLength = ROUND_UP(width * 1, 4);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 通道。
3、將
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;
四、局限性
1. subtractMaskView 不會自動刷新
我們知道,當 UIView 的 maskView 的內容動態修改時,會實時反映到 UIView 中。但在本項目中, subtractMaskView 屬性會生成一張全新的圖片來作為遮罩圖,因為不會根據 subtractMaskView 的內容實時來刷新視圖。如果需要更新,必須手動調用 setSubtractMaskView: 方法來重新生成遮罩圖。
2. setSubtractMaskView: 不宜被頻繁調用
setSubtractMaskView: 本質上是生成一個新的遮罩圖的過程,該過程涉及圖片像素的遍歷轉換,較為耗時,不宜頻繁調用。
綜上所述,這種方案適合只生成一次遮罩圖的場景。
五、源碼
請到 GitHub 上查看完整代碼。
總結
以上所述是小編給大家介紹的iOS中一行代碼實現 UIView 鏤空效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
新聞熱點
疑難解答