iOS開發(fā)--模糊不規(guī)則的View

附上demo地址:https://github.com/BHAreslee/shapeView

? ? ? ? 最近在開發(fā)的一個項目怜奖,要求對面部的眉毛位置進(jìn)行模糊效果處理纱皆。大致分為三個步驟:1:獲取眉毛區(qū)域的關(guān)鍵點嗤无。2:通過連接關(guān)鍵點獲取一個封閉的區(qū)域。3:在這個封閉區(qū)域中增加高斯模糊效果蹬音。

目標(biāo)效果:


每一個UIView之所以能夠顯示出來坦报,其實是CALayer在起作用库说。兩者就相當(dāng)于是畫布和畫框的關(guān)系。UIView只負(fù)責(zé)在畫布上畫一幅畫片择,但最終能否完整將一幅畫展示出來潜的,則是取決于CALayer的形狀,也就是CALayer的frame字管。UIView主要是對顯示內(nèi)容的管理而 CALayer 主要側(cè)重顯示內(nèi)容的繪制啰挪。

? ? ? ? 那如何獲得一個不規(guī)則的View呢?或者說如何讓一幅畫只在不規(guī)則的區(qū)域中顯示呢嘲叔?UIView只能通過修改frame改變大小和位置亡呵,似乎沒有方式去改變形狀。這個時候我們可以調(diào)整畫框即可硫戈。我們通過查看CALayer的相關(guān)屬性锰什,可以得知CALayer有個子類叫CAShapeLayer。這個CAShapeLayer中有一個屬性是CGPathRef類的path丁逝。之前用過UIBezierPath汁胆,很快就想到這個path應(yīng)該就是形狀了。我們可以通過UIBezierPath連線得到果港。OK沦泌,思路有了我們來實現(xiàn)一下吧。

實現(xiàn):

在控制器中首先添加一個屬性@property (nonatomic, strong) UIImageView *imgView;

然后在viewDidLoad方法中辛掠,做一下幾部:

1:創(chuàng)建UIBezierPath對象谢谦,并繪制左右眉毛的區(qū)域。我以簡單的三角形替代眉毛的區(qū)域

UIBezierPath *path1 = [UIBezierPath bezierPath];

path1.lineWidth = 1;

path1.lineCapStyle = kCGLineCapRound;

path1.lineJoinStyle = kCGLineJoinRound;

//畫左眉

CGPoint p1 = CGPointMake(100, 100);

CGPoint p2 = CGPointMake(300, 100);

CGPoint p3 = CGPointMake(300, 300);

[path1 moveToPoint:p1];

[path1 addLineToPoint:p2];

[path1 addLineToPoint:p3];

[path1 closePath];

UIBezierPath *path2 = [UIBezierPath bezierPath];

//畫右眉

CGPoint p4 = CGPointMake(0, 100);

CGPoint p5 = CGPointMake(80, 90);

CGPoint p6 = CGPointMake(100, 300);

[path2 moveToPoint:p4];

[path2 addLineToPoint:p5];

[path2 addLineToPoint:p6];

[path2 closePath];

//此時我們得到了兩個path萝衩,我們用一個方法把他合為一個path回挽。讓path1包含path2。

[path1 appendPath:path2];

2.創(chuàng)建兩個UIImageView猩谊,一個用來展示原圖千劈,一個用來做模糊效果

//self.imgView用來展示原圖

self.imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"timg.jpeg"]];

[self.view addSubview:self.imgView];

self.imgView.frame = self.view.bounds;

//imgViewBlur用來模糊

UIImageView *imgViewBlur = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"timg.jpeg"]];

imgViewBlur.frame = self.view.bounds;

//模糊圖片操作,方法blurryImage:withBlurLevel:會在后面列出

imgViewBlur.image = [self blurryImage:imgViewBlur.image withBlurLevel:1];

3.自定義一個bhView牌捷,繼承自UIView墙牌,并添加一個屬性@property (nonatomic, strong) UIImage *img,在控制器創(chuàng)建bhView對象時暗甥,傳入img喜滨,給bhView中用drawRect方法繪制。

至于為什么要創(chuàng)建bhView撤防,會在后面說明虽风。

在bhView的.m文件中重寫drawRect

- (void)drawRect:(CGRect)rect {

self.backgroundColor = [UIColor clearColor];

[self.img drawInRect:rect];

}

bhView *bh = [[bhView alloc]initWithFrame:self.view.bounds];

bh.backgroundColor = [UIColor clearColor];

bh.img = imgViewBlur.image;

//我們只能通過調(diào)用此方法,來觸發(fā)drawRect方法。系統(tǒng)不讓直接調(diào)用drawRect

[bh setNeedsDisplay];

//創(chuàng)建CAShapeLayer對象maskLayer

CAShapeLayer* maskLayer = [CAShapeLayer layer];

//把準(zhǔn)備好的path1賦值給maskLayer.path的path屬性

maskLayer.path = path1.CGPath;

//在將bhView的對象bh的layer設(shè)置為maskLayer

bh.layer.mask = maskLayer;

至此辜膝,我們已經(jīng)得到了兩張圖无牵,一張圖是UIImageView展示出來的,一張圖是通過bhView畫出來的厂抖,模糊后并且只在特定區(qū)域顯示茎毁。



接下來就要合并這兩張圖。

4:合并圖片,通過上下文的方式將兩個圖片繪制在一起验游,生成一張新圖片

CGSize size = CGSizeMake([UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height);

//開啟上下文

UIGraphicsBeginImageContext(size);

//先畫完整的圖片

[self.imgView.image drawInRect:self.imgView.frame];

//再畫模糊的局部圖片充岛。convertViewToImage:方法實現(xiàn)會貼在后面

[[self convertViewToImage:bh] drawInRect:bh.frame];

//拿到生成的ZImage

UIImage *ZImage = UIGraphicsGetImageFromCurrentImageContext();

//關(guān)閉圖形上下文

UIGraphicsEndImageContext();

//給展示圖賦新圖片

self.imgView.image = ZImage;


結(jié)束「酰看看最終效果


下面貼兩個方法

//View轉(zhuǎn)圖片


-(UIImage*)convertViewToImage:(UIView*)v{

CGSize s = v.bounds.size;

// 下面方法崔梗,第一個參數(shù)表示區(qū)域大小。第二個參數(shù)表示是否是非透明的垒在。如果需要顯示半透明效果蒜魄,需要傳NO,否則傳YES场躯。第三個參數(shù)就是屏幕密度了

UIGraphicsBeginImageContextWithOptions(s, NO, [UIScreen mainScreen].scale);

[v.layer renderInContext:UIGraphicsGetCurrentContext()];

UIImage*image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return image;

}


//模糊圖片處理方法如下


- (UIImage *)blurryImage:(UIImage *)image withBlurLevel:(CGFloat)blur{? ? if (image==nil)? ? {? ? ? ? NSLog(@"error:為圖片添加模糊效果時谈为,未能獲取原始圖片");? ? ? ? return nil;? ? }? ? //模糊度,? ? if (blur < 0.025f) {? ? ? ? blur = 0.025f;? ? } else if (blur > 1.0f) {? ? ? ? blur = 1.0f;? ? }? ? ? ? //boxSize必須大于0? ? int boxSize = (int)(blur * 100);? ? boxSize -= (boxSize % 2) + 1;? ? NSLog(@"boxSize:%i",boxSize);? ? //圖像處理? ? CGImageRef img = image.CGImage;? ? //需要引入#import//圖像緩存,輸入緩存,輸出緩存

vImage_Buffer inBuffer, outBuffer;

vImage_Error error;

//像素緩存

void *pixelBuffer;

//數(shù)據(jù)源提供者踢关,Defines an opaque type that supplies Quartz with data.

CGDataProviderRef inProvider = CGImageGetDataProvider(img);

// provider’s data.

CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

//寬伞鲫,高,字節(jié)/行签舞,data

inBuffer.width = CGImageGetWidth(img);

inBuffer.height = CGImageGetHeight(img);

inBuffer.rowBytes = CGImageGetBytesPerRow(img);

inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

//像數(shù)緩存秕脓,字節(jié)行*圖片高

pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

outBuffer.data = pixelBuffer;

outBuffer.width = CGImageGetWidth(img);

outBuffer.height = CGImageGetHeight(img);

outBuffer.rowBytes = CGImageGetBytesPerRow(img);

// 第三個中間的緩存區(qū),抗鋸齒的效果

void *pixelBuffer2 = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

vImage_Buffer outBuffer2;

outBuffer2.data = pixelBuffer2;

outBuffer2.width = CGImageGetWidth(img);

outBuffer2.height = CGImageGetHeight(img);

outBuffer2.rowBytes = CGImageGetBytesPerRow(img);

//Convolves a region of interest within an ARGB8888 source image by an implicit M x N kernel that has the effect of a box filter.

error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer2, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

error = vImageBoxConvolve_ARGB8888(&outBuffer2, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

if (error) {

NSLog(@"error from convolution %ld", error);

}

//? ? NSLog(@"字節(jié)組成部分:%zu",CGImageGetBitsPerComponent(img));

//顏色空間DeviceRGB

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

//用圖片創(chuàng)建上下文,CGImageGetBitsPerComponent(img),7,8

CGContextRef ctx = CGBitmapContextCreate(

outBuffer.data,

outBuffer.width,

outBuffer.height,

8,

outBuffer.rowBytes,

colorSpace,

CGImageGetBitmapInfo(image.CGImage));

//根據(jù)上下文,處理過的圖片儒搭,重新組件

CGImageRef imageRef = CGBitmapContextCreateImage (ctx);

UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

//clean up

CGContextRelease(ctx);

CGColorSpaceRelease(colorSpace);

free(pixelBuffer);

free(pixelBuffer2);

CFRelease(inBitmapData);

//CGColorSpaceRelease(colorSpace);? //多余的釋放

CGImageRelease(imageRef);

return returnImage;

}



現(xiàn)在說明為什么藥自定義bhView吠架。我剛開始也是直接創(chuàng)建了兩個UIImageView,一個是原圖搂鲫,一個模糊部分區(qū)域的圖傍药。然后在開上下文合并,發(fā)現(xiàn)結(jié)果得到的是一張全部模糊的圖魂仍。

以下直接繪制是不行的:

?UIGraphicsBeginImageContext(self.imgView.bounds.size);

? [self.imgView.image drawInRect:self.view.bounds];

? [imgViewBlur.image drawInRect:self.view.bounds];

? ?UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();

?? UIGraphicsEndImageContext();

因為拐辽,在上下文中繪制的是imgViewBlur.imageimage對象,imgViewBlur.imageimage對象是沒有形狀的擦酌,雖然你只能看到兩個模糊的三角形區(qū)域薛训。但實際上整個imgViewBlur.image都被模糊了,因為layerd的關(guān)系仑氛,我們才看不到其他區(qū)域而已。

后來我想到,通過使用UIView的drawRect方法同樣可以得到一張圖片锯岖,再通過設(shè)置layer就能夠只展現(xiàn)模糊的三角區(qū)域介袜。再將bhView對象與UIImageView對象合并即可。

? 附上demo地址:https://github.com/BHAreslee/shapeView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末出吹,一起剝皮案震驚了整個濱河市遇伞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捶牢,老刑警劉巖鸠珠,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異秋麸,居然都是意外死亡渐排,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門灸蟆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驯耻,“玉大人,你說我怎么就攤上這事炒考】筛浚” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵斋枢,是天一觀的道長帘靡。 經(jīng)常有香客問我,道長瓤帚,這世上最難降的妖魔是什么描姚? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上蟹演,老公的妹妹穿的比我還像新娘既琴。我一直安慰自己,他們只是感情好赃阀,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著擎颖,像睡著了一般榛斯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搂捧,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天驮俗,我揣著相機與錄音,去河邊找鬼允跑。 笑死王凑,一個胖子當(dāng)著我的面吹牛搪柑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播索烹,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼工碾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了百姓?” 一聲冷哼從身側(cè)響起渊额,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垒拢,沒想到半個月后旬迹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡求类,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年奔垦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仑嗅。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宴倍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仓技,到底是詐尸還是另有隱情鸵贬,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布脖捻,位于F島的核電站阔逼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏地沮。R本人自食惡果不足惜嗜浮,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摩疑。 院中可真熱鬧危融,春花似錦、人聲如沸雷袋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楷怒。三九已至蛋勺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸠删,已是汗流浹背抱完。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刃泡,地道東北人巧娱。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓碉怔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親禁添。 傳聞我的和親對象是個殘疾皇子眨层,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容

  • iOS開發(fā)中有的時候需要將圖片設(shè)置模糊,來實現(xiàn)特定的效果獲取更好的用戶體驗, iOS7之后半透明模糊效果得到大范圍...
    零距離仰望星空閱讀 46,529評論 47 223
  • 1上荡、禁止手機睡眠[UIApplication sharedApplication].idleTimerDisabl...
    DingGa閱讀 1,117評論 1 6
  • 1.NSString過濾特殊字符串定義一個特殊字符的集合NSCharacterSet set = [NSChara...
    奮拓達(dá)閱讀 726評論 0 0
  • 說母親,她是一個很欠缺安全感的人馒闷。在一個男性為主導(dǎo)的社會酪捡,甚至于家庭,她所有的一切都來自于另一個人的給予(...
    danaom閱讀 275評論 0 0
  • 因為孤獨是生命的常態(tài)纳账,所以陪伴才顯得長情逛薇。生活在燈紅酒綠的城市里,不知道你是否找到了自己的方向疏虫?在人潮人海中奔波永罚,...
    柚小兮閱讀 895評論 0 2