I tested it. When I moved the View, the tap event on the view doesn't respond, and it only responded when it stopped. I don't know why this happens. Below is my code
@interface ViewController ()
@property(nonatomic,strong)UIView *testView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.testView = [[UIView alloc]init];
self.testView.backgroundColor = [UIColor redColor];
self.testView.frame = CGRectMake(0, 100, 100, 100);
[self.view addSubview:self.testView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapTestView:)];
[self.testView addGestureRecognizer:tap];
}
- (void)tapTestView:(UITapGestureRecognizer *)tap {
NSLog(@"Event triggered");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[UIView animateWithDuration:6 animations:^{
CGRect frame = self.testView.frame;
frame.origin.x = 400;
self.testView.frame = frame;
}];
}
First, you want to make sure to use animatewithduration:delay:options:animations:completion:
with the UIViewAnimationOptionAllowUserInteraction
option. If you do not use this option, animated views will not receive touches.
[UIView animateWithDuration:26 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = self.testView.frame;
frame.origin.x = ...;
self.testView.frame = frame;
} completion:nil];
Second, when views are animated, if you examine the frame
of that view as it is moving, you will see that the frame
property won’t reflect where the view really is at that moment of time, but, rather, where it will eventually be. Specifically, the frame
property will be that value you set in that animations
block, even if it hasn’t finished actually moving there, yet.
The implication of this behavior is that, even if you enable user interaction during animation, as shown above, gestures will be recognized based upon the view’s final destination rather than where it is now. E.g., if you start animation of frame from rect A to rect B, the gesture recognizer will recognize taps within rect B even if the view is not there yet! And conversely, it won’t recognize taps where the animated view happens to be, because the frame
effectively thinks the view is already at the destination.
To get around that issue, one must refer to the view’s layer’s presentationLayer
to get the location of the view mid-animation. One approach is to add a gesture recognizer to the main view and then, in there, see if the touch was inside the animated view’s presentation layer:
- (void)tapMainView:(UITapGestureRecognizer *)tap {
CGPoint point = [tap locationInView:self.testView.superview];
CGRect frame = self.testView.layer.presentationLayer.frame;
BOOL isInTestView = CGRectContainsPoint(frame, point);
if (isInTestView) {
[self testViewAction];
return;
}
// otherwise, we're in the super view
[self mainViewAction];
}
Alternatively, you can (a) define a UIView
subclass for the child view and (b) override its hitTest
implementation to factor in the presentation layer:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self.layer.presentationLayer hitTest:[self convertPoint:point toView:self.superview]]) {
return self;
} else {
return nil;
}
}