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的值
动画的过程只是看起来是动态变换的,其内部的值已经是固定的了。
简单说下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,接下来就是判断点击的点是否在 动画view.layer.presentationLayer 中了, CGRectContainsPoint 或者 调用Layer的hitTest方法就能判断是不是一次有效点击了。
既然已经知道了原理, 那就试试. 我这里是重写了touchesBegan, 也可以添加手势到动画view的父视图上.
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *redView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.redView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_redView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.redView];
self.redView.userInteractionEnabled = NO ;
}
- (IBAction)buttonAction:(id)sender {
[UIView animateWithDuration:15 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.redView.frame = CGRectMake(300, 500, 100, 100);
} completion:^(BOOL finished) {
self.redView.frame = CGRectMake(100, 100, 100, 100);
}];
}
// 想做的动画
- (void)tapAction {
NSLog(@"想要处理的动画过程中点击");
self.redView.backgroundColor = [UIColor colorWithRed:(arc4random()%255)/ 255.f green:(arc4random()%255)/ 255.f blue:(arc4random()%255)/ 255.f alpha:1];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 获取到点击的位置
UITouch * touch = touches.anyObject;
CGPoint point = [touch locationInView:self.view];
NSLog(@"point %@",NSStringFromCGPoint(point));
NSLog(@"point %@",NSStringFromCGRect(self.redView.layer.presentationLayer.frame) );
// 判断redView.layer.presentationLayer是否包含这个点
// 方式1:CGRectContainsPoint
if ( CGRectContainsPoint(self.redView.layer.presentationLayer.frame, point) ) {
[self tapAction];
}
// 方式2:hitTest
// if ([self.redView.layer.presentationLayer hitTest:point] != nil) {
// [self tapAction];
// }
}
@end
locationInView:与translationInView:的区别
translationInView : 手指在视图上移动的位置(x,y)向下和向右为正,向上和向左为负。
locationInView : 手指在视图上的位置(x,y)就是手指在视图本身坐标系的位置。
velocityInView: 手指在视图上移动的速度(x,y), 正负也是代表方向,值得一体的是在绝对值上|x| > |y| 水平移动, |y|>|x| 竖直移动。