1. 程式人生 > >ios捕捉移動view的點選事件

ios捕捉移動view的點選事件

對Core Animation來說,不管是顯式動畫還是隱式動畫,對其設定frame都是立即設定的,比如說給一個UIView做移動動畫,雖然看起來frame在持續改變,但其實它的frame已經是最終值了,這種情況下,哪怕這個UIView是UIButton的例項,其觸發touch事件的範圍還是最終frame的地方。比如一個Button的frame是(0,0,100,100),要把它從0,0移動到200,200,在這種情況下:
  1. 如果你使用的是顯式動畫(CAKeyframeAnimation和CABasicAnimation),是通過指定path或values來進行動畫的,它的frame並沒有改變,touch範圍還是(0,0,100,100)這個範圍內
  2. 如果你使用的是隱式動畫(UIView的animate方法),是通過設定frame來進行動畫的,那麼它的touch範圍就是(200,200,100,100)這個範圍內
這個區別很重要,你只用記住,如果是用UIView做動畫,設定的frame是有效的;
如果CALaye做動畫設定的frame是無效的,你應該在動畫結束後顯式地指定position的值

如果我們要為移動動畫中的View新增touch事件,就需要在後面用到hitTest。我們先建立一個動畫Layer: [objc]
view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. CGSize layerSize = CGSizeMake(100100);  
  2. CALayer *movingLayer = [CALayer layer];  
  3. movingLayer.bounds = CGRectMake(00, layerSize.width, layerSize.height);  
  4. [movingLayer setBackgroundColor:[UIColor orangeColor].CGColor];  
  5. movingLayer.anchorPoint = CGPointMake(00);  
  6. [self.view.layer
     addSublayer:movingLayer];  
  7. self.movingLayer = movingLayer;  
這裡面只有anchorPoint重要一些,因為anchorPoint能影響position的取值,對Layer來說,frame是抽象的,只有bounds和position是真實存在的,並且設定frame和設定anchorPoint的順序不同,開始看到的結果也不同:
左邊是先設定的anchorPoint再設定frame,右邊是相反,可以看到如果是先設定anchorPoint的話,在剛開始就能得到正確的結果,即position為0,0。但如果是設定bounds的話就不用這麼麻煩了,順序無所謂。我在這裡之所以要設定anchorPoint,是因為在後面的CAKeyframeAnimation將用position來做動畫,如果anchorPoint為預設值(即0.5,0.5)的話,在動畫中將會出現一個x、y軸一半的偏移,就像這樣:

下面用一張圖解釋anchorPoint的取值:
position是以anchorPoint為原點的,預設這個原點在中心位置,自然就會出現上圖x、y軸的偏移,其實只要我在動畫中將position做個偏移就不用設定anchorPoint,但是我覺得動畫從0到終點更直觀一些,所以這才是我設定anchorPoint的原因,並不是說非設不可。最後用一個weak屬性引用動畫Layer並把這個Layer新增到self.view.layer中。 接下來是動畫部分: [objc] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. CAKeyframeAnimation *moveLayerAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];  
  2. //moveLayerAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
  3. //moveLayerAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(320 - self.movingLayer.bounds.size.width, 0)];
  4. moveLayerAnimation.values = @[[NSValue valueWithCGPoint:CGPointMake(00)],  
  5.                               [NSValue valueWithCGPoint:CGPointMake(320 - self.movingLayer.bounds.size.width0)]];  
  6. moveLayerAnimation.duration = 2.0;  
  7. moveLayerAnimation.autoreverses = YES;  
  8. moveLayerAnimation.repeatCount = INFINITY;  
  9. moveLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];  
  10. [self.movingLayer addAnimation:moveLayerAnimation forKey:@"move"];  
如果是用CABasicAnimation做動畫,則用fromValue及toValue替換setValues,timingFunction直接用線性,不用做其他變換,關於這個屬性的預置值,我在另一篇博文中有提到。
接下來為self.view新增手勢識別: [objc] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. ........  
  2.     self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(click:)];  
  3.     [self.view addGestureRecognizer:self.tapGesture];  
  4. }  
  5. -(void)click:(UITapGestureRecognizer *)tapGesture {  
  6.     CGPoint touchPoint = [tapGesture locationInView:self.view];  
  7.     if ([self.movingLayer.presentationLayer hitTest:touchPoint]) {  
  8.         NSLog(@"presentationLayer");  
  9.     }  
  10. }  
我在最開始的時候有提到,動畫的過程只是看起來是動態變換的,其內部的值已經是固定的了。 簡單說下Core Animation,Core Animation用三個Tree來完成動畫:
  • Layer Tree
  • Presentation Tree
  • Render Tree
Layer Tree用來儲存Layer最終的值,即不考慮動畫的發生,你只要對一個Layer進行了一些諸如backgroundColor、position、alpha之類的賦值,其Layer Tree中儲存的值立刻改變;Presentation Tree用來儲存所有的要在動畫過程中顯示的值,與Layer Tree相反;Render Tree專門用來渲染Presentation Tree中的值,也是與Core Animation互動的唯一紐帶,這一過程被系統隱藏了,我們不用管也沒辦法管。 這就是說Layer Tree與Presentation Tree其實都相當於是模型物件,只儲存Layer的狀態,當我們要讀取動畫進行中的狀態的時候,呼叫layer的presentationLayer屬性就可以了,這個屬性從Presentation Tree中返回代表當前動畫狀態的Layer,接下來呼叫Layer的hitTest方法就能判斷是不是一次有效點選了。