由于項(xiàng)目的一個(gè)需求,研究了下CALayer ,發(fā)現(xiàn)了一個(gè)比較有趣的屬性
contents先展示下完成的需求效果。

可以看到,就是一個(gè)對(duì)一個(gè)圖片模糊處理后,點(diǎn)擊的時(shí)候,點(diǎn)擊區(qū)域變?yōu)榍逦?,而未觸摸區(qū)域依然保持模糊狀態(tài)。如果單純的處理一張圖片模糊我們知道有很多種方法可以實(shí)現(xiàn),這里的主要難點(diǎn)是,在手指觸摸的區(qū)域需要還原圖片的清晰,并且如果在手指長(zhǎng)按滑動(dòng)的情況下,需要根據(jù)手指移動(dòng)的軌跡來(lái)還原圖片的清晰。
剛接手這個(gè)需求的時(shí)候,首先想到的是,根據(jù)手指移動(dòng)的軌跡 重新繪制點(diǎn)擊的區(qū)域來(lái)完成,但是實(shí)驗(yàn)以后發(fā)現(xiàn)并不太容易實(shí)現(xiàn)理想效果,而且性能很差。
使用 CGContextRef 處理問(wèn)題
1,首先性能很差,在手指長(zhǎng)按滑動(dòng)時(shí),如果時(shí)時(shí)的根據(jù)移動(dòng)位置做重繪操作,必定造成內(nèi)存的激增。這個(gè)避免不了。
2, 不能很好的處理觸摸區(qū)域周邊的糊化效果,
3,當(dāng)做圖片放大時(shí),對(duì)觸摸清晰的區(qū)域不好把控。
基于上述這些問(wèn)題,后面果斷放棄使用圖形上下文來(lái)做這個(gè)需求。
困擾了很久,看了一些GPUImage上面的效果 也沒有找到想要的答案。
后來(lái)有一天忽然想到一個(gè)問(wèn)題,UIImageView是怎么顯示圖片出來(lái)的,也就是它的內(nèi)部實(shí)現(xiàn)。我們都知道系統(tǒng)的顯示控件都是繼承自UIView 而一個(gè)view之所以能夠顯示,是因?yàn)槠鋬?nèi)部的layer ,所有的顯示有CALayer來(lái)處理。想到這一點(diǎn)就馬上查閱文檔,發(fā)現(xiàn)了一個(gè)其內(nèi)部的 Contents,和 mask 屬性。
通過(guò)測(cè)試,果然可以實(shí)現(xiàn)需求效果。。這里說(shuō)下大概思路。
1, 首先將圖片模糊化 ,在模糊之前保存當(dāng)前的圖片,以備做觸摸清晰時(shí)使用。。 這里我使用的是
Accelerate庫(kù)的一些函數(shù),來(lái)處理模糊。設(shè)置和frame。
2, 創(chuàng)建一個(gè)和模糊圖片相同的UIImageView對(duì)象frame需相同,將該對(duì)象layer內(nèi)部的mask 替換為自定義的CALayer對(duì)象 ,
3, 將自定義的CALayer對(duì)象內(nèi)部的contents替換為一個(gè)"占位圖片", 初始化時(shí),先隱藏自定義的layer 對(duì)象。這樣由于imageView的layer屬性已經(jīng)替換為自定義的了,當(dāng)將自定義的layer的hidden設(shè)置為YES時(shí),當(dāng)前這張圖片也就不會(huì)顯示出來(lái)了。
4, 在手勢(shì)事件觸發(fā)的時(shí)候,將自定義的layer顯示出來(lái),并且將之前保存的清晰圖片賦值給自定義的圖片對(duì)象,由于圖片對(duì)象的layer已經(jīng)被替換,所以顯示出來(lái)的,就是我們想要的結(jié)果了。
CALayer *imageMaskLayer = [CALayer layer];
NSString *path = [[NSBundle mainBundle] pathForResource:@"patternFinnal" ofType:@".png"];
UIImage *displayerImage = [UIImage imageWithContentsOfFile:path];
imageMaskLayer.contents = (__bridge id)displayerImage.CGImage;
imageMaskLayer.frame = CGRectMake(0, 0, touchSize, touchSize);
imageMaskLayer.hidden = YES;
self.imageMaskLayer = imageMaskLayer;
_showImageView.layer.mask = imageMaskLayer;
在觸發(fā)手勢(shì)后的操作。
CGPoint touchPoint = [pre locationInView:self.imageView];
if (pre.state == UIGestureRecognizerStateBegan || pre.state == UIGestureRecognizerStateChanged) {
self.imageMaskLayer.hidden = NO;
self.showImageView.image = self.matterImage;
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.imageMaskLayer.position = CGPointMake(touchPoint.x, touchPoint.y - 50);
[CATransaction commit];
if ([self.delegate respondsToSelector:@selector(toucClearView: touchPiontDidChange:)]) {
[self.delegate toucClearView:self touchPiontDidChange:touchPoint];
}
這里有必要說(shuō)下,
當(dāng)手指滑動(dòng)時(shí),我是修改layer內(nèi)部的position來(lái)處理的,真的是免去了,之前根據(jù)滑動(dòng)位置來(lái)計(jì)算位置的煩惱。但是在設(shè)置 layer的position 之前必須要設(shè)置[CATransaction begin] 才有效。
這段時(shí)間我將這段邏輯抽取了出來(lái),封裝了一個(gè)小的demo ,如果對(duì)你有些幫助,還望給個(gè)star.