iOS-事件响应链的学习

前言ide

事件处理完整过程;学习

1,在手指触摸屏幕时,会产生一个事件,系统会把这个事件添加到UIApplication管理的事件队列中。
2,取出队列中最前面的事件,交给主窗口Window。
3,主窗口会逐级向上来寻找最适合的视图控件view。
4,找到最适合的view后,这个view就是最适合的响应者。
5,若是这个最适合的响应者不响应事件,那么它就会把这个事件交给它的父控件来响应。
6,若是都不对这个事件做出响应的话,最后仍是交给UIApplication来丢弃这个事件。code

关键的是两个方法;队列

(1)- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;查找最合适的响应视图;事件

(2)- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{    return YES;
};判断点击位置是否在当前的视图内;在的话在查找子视图,不在跳转至同级视图或者是父视图。get

 

(一)UIButton是一个比较特殊的UI控件,相对于通常的view,它能够便可以使用target-action添加事件,也可使用addGestureRecognizer添加手势并执行,可是你会发现,二者共存的时候,点击UIButton会优先执行手势的事件,而不会再执行target-action的事件,也就是说UIButton执行完手势事件后,会中断响应链。it

TestButton *testBtn = [[TestButton alloc] initWithFrame:CGRectMake(50, 50, 50, 30)];
    testBtn.backgroundColor = [UIColor blueColor];
    [testBtn addTarget:nil action:@selector(clickButton) forControlEvents:UIControlEventTouchUpInside];
    [viewB addSubview:testBtn];
    
    
    UITapGestureRecognizer *buttonTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickTap:)];
    [testBtn addGestureRecognizer:buttonTap];



- (void)clickButton
{
    NSLog(@"button click");
}

- (void)clickTap:(UIGestureRecognizer *)ges
{
    NSLog(@"button ges %@",ges);
}

上述的代码,点击testBtn,会执行clickTap:这个方法;即执行了手势代码。这是为何呢,其实我也不是很明白,下面我总结我查到的资料。io

(1)有的人说手势事件和target-action事件是在UIButton的UITouch委托方法中执行- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event或者是- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event,在手势事件不为nil的状况下,优先执行手势。可是我重写了UIButton的UITouch的委托方法,若不执行[super touchesEnded:touches withEvent:event];的状况下target-action事件不会执行反之就执行,而手势无论怎样都会执行。因此这样的状况下我只能说,系统在二者共存的状况下会执行手势。event

(2)还看到一种说法就是,手势是一系列的UIEvent事件,每次在传递响应事件,都先将事件传递给在事件链顶端的手势,没有识别就传递至下一个响应者的手势,如都不识别,回到起点开始查找可以执行的touch事件。这样的话,貌似就能够说得通,咱们就当上面UIButton的taget-action的实现方式是在- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event或者是- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event这些委托事件中,在UIButton设置了手势能够被识别,就会放弃查找touch事件链,从而响应手势事件。手势未被识别,就查找touch事件链,执行target-action事件。table

 

(二)总结一下

事件的响应原理:
1.调用最合适控件的touches.... 方法;
2.若是调用了[super touches....];就会将事件顺着响应者链条往上传递,传递给上一个响应者;
3.接着就会调 上一个响应者的touches.... 方法;

重写view的touch委托事件,能够解决一写子视图超出父视图后,点击部分位置没法响应事件的等等问题。学习事件响应链能够写出很赞的效果;好比UIApplication的sendEvent:方法配合NSNotification能够有意想不到的效果。

个人总结是,手势会优先的截获touch事件,若手势识别失败,则会进入touch事件的- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event委托方法,若识别成功,则执行手势并会进入touch事件的- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event来取消本次的touch事件,事件响应链停止,不少的手势与touch事件冲突好比说UITableViewCell的- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath与手势共存点击失效的问题,就是这样引发的,touch事件响应因手势执行成功而被停止。解决的方法就是:重写UIGestureRecognizerDelegate中的
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch方法。

#pragma mark - UIGestureRecognizerDelegate  
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch  
{  
    // 输出点击的view的类名  
    NSLog(@"%@", NSStringFromClass([touch.view class]));  
      
    // 若为UITableViewCellContentView(即点击了tableViewCell),则不截获Touch事件  
    if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) {  
        return NO;  
    }  

    return  YES;  
}

判断点击的视图是否为UITableViewCell,是的话手势就不截获touch事件;如不是则截获touch事件。

相关文章
相关标签/搜索