iOS视图移动时为什么不能触发事件

Why can't the event be triggered when the iOS view moves

我测试过了。当我移动视图时,视图上的点击事件没有响应,只有在停止时才响应。我不知道为什么会这样。下面是我的代码

@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;
    }];
}

首先,您要确保使用 animatewithduration:delay:options:animations:completion: with the UIViewAnimationOptionAllowUserInteraction 选项。如果不使用此选项,动画视图将不会接收到触摸。

[UIView animateWithDuration:26 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
    CGRect frame = self.testView.frame;
    frame.origin.x = ...;
    self.testView.frame = frame;
} completion:nil];

其次,当视图被动画化时,如果您在移动时检查该视图的 frame,您会发现 frame 属性 不会反映视图确实是在那一刻,而是它最终会在哪里。具体来说,frame 属性 将是您在 animations 块中设置的值,即使它还没有真正移动到那里。

此行为的含义是,即使您在动画期间启用用户交互(如上所示),也会根据视图的最终目的地而不是现在的位置来识别手势。例如,如果您开始从矩形 A 到矩形 B 的帧动画,手势识别器将识别矩形 B 内的点击,即使视图还不存在!相反,它不会识别动画视图恰好所在的点击,因为 frame 实际上认为视图已经在目的地。

为了解决这个问题,必须参考视图层的 presentationLayer 来获取视图中间动画的位置。一种方法是向主视图添加手势识别器,然后在其中查看触摸是否在动画视图的表示层内:

- (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];
}

或者,您可以 (a) 为子视图定义一个 UIView 子类,并且 (b) 覆盖其 hitTest 实现以考虑表示层:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if ([self.layer.presentationLayer hitTest:[self convertPoint:point toView:self.superview]]) {
        return self;
    } else {
        return nil;
    }
}