由于項目的一個需求,研究了下CALayer ,發現了一個比較有趣的屬性
contents
先展示下完成的需求效果。
可以看到,就是一個對一個圖片模糊處理后,點擊的時候,點擊區域變為清晰,而未觸摸區域依然保持模糊狀態。如果單純的處理一張圖片模糊我們知道有很多種方法可以實現,這里的主要難點是,在手指觸摸的區域需要還原圖片的清晰,并且如果在手指長按滑動的情況下,需要根據手指移動的軌跡來還原圖片的清晰。
剛接手這個需求的時候,首先想到的是,根據手指移動的軌跡 重新繪制點擊的區域來完成,但是實驗以后發現并不太容易實現理想效果,而且性能很差。
使用 CGContextRef 處理問題
1,首先性能很差,在手指長按滑動時,如果時時的根據移動位置做重繪操作,必定造成內存的激增。這個避免不了。
2, 不能很好的處理觸摸區域周邊的糊化效果,
3,當做圖片放大時,對觸摸清晰的區域不好把控。
基于上述這些問題,后面果斷放棄使用圖形上下文
來做這個需求。
困擾了很久,看了一些GPUImage
上面的效果 也沒有找到想要的答案。
后來有一天忽然想到一個問題,UIImageView
是怎么顯示圖片出來的,也就是它的內部實現。我們都知道系統的顯示控件都是繼承自UIView
而一個view
之所以能夠顯示,是因為其內部的layer ,所有的顯示有CALayer來處理。想到這一點就馬上查閱文檔,發現了一個其內部的 Contents
,和 mask
屬性。
通過測試,果然可以實現需求效果。。這里說下大概思路。
1, 首先將圖片模糊化 ,在模糊之前保存當前的圖片,以備做觸摸清晰時使用。。 這里我使用的是
Accelerate
庫的一些函數,來處理模糊。設置和frame。
2, 創建一個和模糊圖片相同的UIImageView
對象frame
需相同,將該對象layer內部的mask 替換為自定義的CALayer
對象 ,
3, 將自定義的CALayer
對象內部的contents
替換為一個"占位圖片", 初始化時,先隱藏自定義的layer 對象。這樣由于imageView的layer屬性已經替換為自定義的了,當將自定義的layer
的hidden
設置為YES
時,當前這張圖片也就不會顯示出來了。
4, 在手勢事件觸發的時候,將自定義的layer
顯示出來,并且將之前保存的清晰圖片賦值給自定義的圖片對象,由于圖片對象的layer已經被替換,所以顯示出來的,就是我們想要的結果了。
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;
在觸發手勢后的操作。
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];
}
這里有必要說下,
當手指滑動時,我是修改layer內部的position
來處理的,真的是免去了,之前根據滑動位置來計算位置的煩惱。但是在設置 layer
的position
之前必須要設置[CATransaction begin]
才有效。
這段時間我將這段邏輯抽取了出來,封裝了一個小的demo ,如果對你有些幫助,還望給個star.