iOS中一行代碼實(shí)現(xiàn) UIView 鏤空效果

這是一種實(shí)現(xiàn) UIView 鏤空效果的方案,可以快速實(shí)現(xiàn)任意形狀的鏤空、文字的鏤空、帶鏤空的毛玻璃效果等。本質(zhì)上是 UIView 的 maskView 效果。
前言
首先來復(fù)習(xí)一下遮罩效果的實(shí)現(xiàn)。如果我們有一張圖片,又恰好有一個(gè)圓,當(dāng)我們把圓設(shè)置為圖片的遮罩時(shí),會(huì)得到這樣的結(jié)果。

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

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

注:為了更直觀的效果,圖片中透明的部分用灰白相間格子來表示(以下相同)。
1、將 maskView 轉(zhuǎn)化為 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 對(duì)應(yīng)的 image 圖像。此時(shí)遮罩圖的大小會(huì)被同步為 originView 的大小。
2、將
UIImage 轉(zhuǎn)換為只有 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);
這時(shí)候的 alphaOnlyContext 對(duì)應(yīng)的圖像是下面這樣,只保留了 alpha 通道。
3、將
CGContextRef 中的 alpha 值進(jìn)行遍歷轉(zhuǎn)換
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];
轉(zhuǎn)換后,獲得的 result 圖像是:

于是,我們就可以用 result 愉快地進(jìn)行 mask 了。
三、使用
我們可以將上述的步驟,封裝為一個(gè)方法,用 category 來實(shí)現(xiàn)。
@interface UIView (MFSubtractMask) - (void)setSubtractMaskView:(UIView *)view; - (UIView *)subtractMaskView; @end
這樣調(diào)用起來就十分方便了,一行代碼搞定:
view.subtractMaskView = maskView;
四、局限性
1. subtractMaskView 不會(huì)自動(dòng)刷新
我們知道,當(dāng) UIView 的 maskView 的內(nèi)容動(dòng)態(tài)修改時(shí),會(huì)實(shí)時(shí)反映到 UIView 中。但在本項(xiàng)目中, subtractMaskView 屬性會(huì)生成一張全新的圖片來作為遮罩圖,因?yàn)椴粫?huì)根據(jù) subtractMaskView 的內(nèi)容實(shí)時(shí)來刷新視圖。如果需要更新,必須手動(dòng)調(diào)用 setSubtractMaskView: 方法來重新生成遮罩圖。
2. setSubtractMaskView: 不宜被頻繁調(diào)用
setSubtractMaskView: 本質(zhì)上是生成一個(gè)新的遮罩圖的過程,該過程涉及圖片像素的遍歷轉(zhuǎn)換,較為耗時(shí),不宜頻繁調(diào)用。
綜上所述,這種方案適合只生成一次遮罩圖的場(chǎng)景。
五、源碼
請(qǐng)到 GitHub 上查看完整代碼。
總結(jié)
以上所述是小編給大家介紹的iOS中一行代碼實(shí)現(xiàn) UIView 鏤空效果,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
iOS開發(fā)之隨機(jī)生成兩圓之間的標(biāo)準(zhǔn)圓
這篇文章主要給大家介紹了iOS如何實(shí)現(xiàn)在兩圓之間隨機(jī)生成標(biāo)準(zhǔn)圓的方法,實(shí)現(xiàn)的效果類似尋找附近人或者附近商家的動(dòng)態(tài)效果,有需要的朋友可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。2017-01-01
iOS如何去掉導(dǎo)航欄(UINavigationBar)下方的橫線
本篇文章主要介紹了iOS如何去掉導(dǎo)航欄(UINavigationBar)下方的橫線,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
關(guān)于iOS自適應(yīng)cell行高的那些事兒
這篇文章主要給大家介紹了關(guān)于iOS自適應(yīng)cell行高的那些事兒,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一看看吧2018-11-11
iOS動(dòng)態(tài)驗(yàn)證碼實(shí)現(xiàn)代碼
本文通過實(shí)例代碼給大家介紹了ios動(dòng)態(tài)驗(yàn)證碼的實(shí)現(xiàn)方法,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2018-04-04
iOS App使用設(shè)計(jì)模式中的模板方法模式開發(fā)的示例
這篇文章主要介紹了iOS應(yīng)用使用設(shè)計(jì)模式中的模板方法模式開發(fā)的示例,例子代碼為Objective-C語言,文中還與Java的相關(guān)實(shí)現(xiàn)進(jìn)行類比,需要的朋友可以參考下2016-03-03
在iOS開發(fā)的Quartz2D使用中實(shí)現(xiàn)圖片剪切和截屏功能
這篇文章主要介紹了在iOS開發(fā)的Quartz2D使用中實(shí)現(xiàn)圖片剪切和截屏功能的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12
iOS系統(tǒng)緩存方面開發(fā)的相關(guān)基礎(chǔ)
這篇文章主要介紹了iOS系統(tǒng)緩存方面開發(fā)的相關(guān)基礎(chǔ),示例代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-10-10

