案列
有一个button通过动画移动到另外一个位置,希望可以在移动的过程中也能点击按钮做某件事,一般情况下我们会按照下面的方法处理
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化按钮
button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
button.backgroundColor = [UIColor redColor];
[button addTarget:self action:@selector(buttonEvent:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
// 执行动画
[UIView animateWithDuration:10.f
delay:0
options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction
animations:^{
button.frame = CGRectMake(0, 468, 100, 100);
} completion:^(BOOL finished) {
}];
}
- (void)buttonEvent:(UIButton *)button {
NSLog(@"do something);
}
然后在实际过程中,发现真实的情况是这样的:
- 在移动过程中单击按钮是没有Log输出的
- 单击按钮动画的终点位置(就算此时按钮在界面上的位置离终点还很远)是有Log输出的
然而这并不是我们想要的
分析
在动画过程中界面上显示移动的是layer
中的presentationLayer
,而layer
中的modelLayer
在动画开始的那一瞬间,frame
已经变成CGRectMake(0, 468, 100, 100)
换句话说,动画效果是presentationLayer
改变位置的结果,而modelLayer
是没有动画效果的
解决方法
在touchesBegan
方法中去判断当前点击的点是否在presentationLayer
的frame
里面,在这之前应该做如下两件事
- 注释
button
的addTarget
方法(不然会出现上面提到的情况2) - 设置
button.userInteractionEnabled = NO
(不然在终点处button的单击会阻止触摸事件)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CALayer *presentationLayer = button.layer.presentationLayer;
CALayer *modelLayer = button.layer.modelLayer;
NSLog(@"%@", NSStringFromCGRect(presentationLayer.frame));
NSLog(@"%@", NSStringFromCGRect(modelLayer.frame));
//判断这个点是否在presentationLayer里面
CGPoint point = [[touches anyObject] locationInView:self.view];
if (CGRectContainsPoint(presentationLayer.frame, point)) {
[self buttonEvent:nil];
}
}