美文网首页iOS开发iOS DeveloperiOS学习专题
从iOS的事件响应链看TableView为什么不响应touche

从iOS的事件响应链看TableView为什么不响应touche

作者: WhisperKarl | 来源:发表于2016-09-23 18:16 被阅读4044次

问题还原:当我们需要收起TextField的键盘时,通常的做法一般是在touchBegan方法中放弃第一响应者或者直接endEditing。而当我们把一个TableView添加到控制器的View上时,touchBegan方法会不响应,原因就在于事件被TableView拦截了

iOS的事件响应链

事件响应链,顾名思义就是由一系列事件响应者构成的一个响应层次。当我们点击了手机屏幕上一点时,系统会通过一系列的方法找到应该由哪一个视图来响应我们的点击事件。系统是通过hitTest由UIWindow一层层向下遍历找到可以响应点击事件的子视图,知道某一个视图没有可以响应事件的子视图时,那么这个视图就是我们所说的第一响应者。我们可以写个例子来看这个过程。

事件响应链的形成过程

假设我们有下图的层次结构


层级关系.png

UIView有两个方法:

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   // default returns YES if point is in bounds

通过注释我们可以看出,第一个方法是递归返回hitTest的View对象,第二个方法是返回点击的点是否在某一个View坐标范围内。我们可以通过给UIView写一个分类来打印hitTest过程:

- (BOOL)kr_pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    
    BOOL canAnswer = [self kr_pointInside:point withEvent:event];
    NSLog(@"%@ can answer: %d",self.class,canAnswer);
    return canAnswer;
}

- (UIView *)kr_hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    UIView *answerView = [self kr_hitTest:point withEvent:event];
    NSLog(@"hit view :%@",self.class);
    return answerView;
}

当我们点击ViewC时,我们可以看到打印信息:

UIWindow can answer: 1
UIView can answer: 1
hit view :_UILayoutGuide
hit view :_UILayoutGuide
AView can answer: 1
DView can answer: 0
hit view :DView
hit view :UILabel
BView can answer: 1
hit view :UILabel
CView can answer: 1
hit view :UILabel
hit view :CView
hit view :BView
hit view :AView
hit view :UIView
hit view :UIWindow
UIStatusBarWindow can answer: 1
UIStatusBar can answer: 0
UIStatusBarForegroundView can answer: 0
UIStatusBarServiceItemView can answer: 0
UIStatusBarDataNetworkItemView can answer: 0
UIStatusBarBatteryItemView can answer: 0
UIStatusBarTimeItemView can answer: 0

从打印信息我们大概可以得到响应者查找顺序为:UIWindow->UIView->AView->DView->BView->CView
所以当点击ViewC时我们可以得到一个响应者栈:

C.png

所以第一响应者就是ViewC,如果ViewC不能响应,那么逐级向上查找,如果UIWindow也不响应,事件抛弃。

TableView为什么不响应touchBegan

回到刚开始的问题,当我们点击TableView时,为什么touchBegan不响应呢?通过响应链我们不难想象到,当我们点击屏幕时,第一响应者应该是UITableView,而我们调用的touchBegan其实是ViewController的View的方法,所以无法被调用。
解决方法也很简单,我们可以给tableView写一个基类,重写tableview的touchBegan方法,通过block或者代理传出,然后继承基类,即可实现touchBegan的响应。

相关文章

  • 响应链

    iOS事件响应链中Hit-Test View的应用从iOS的事件响应链看TableView为什么不响应touche...

  • 从iOS的事件响应链看TableView为什么不响应touche

    问题还原:当我们需要收起TextField的键盘时,通常的做法一般是在touchBegan方法中放弃第一响应者或者...

  • iOS 响应链

    iOS开发 - 事件传递响应链iOS 响应者链,事件的传递事件传递之响应链Cocoa Touch事件处理流程--响...

  • iOS开发tableView删除按钮不响应事件

    做了个tableView侧滑删除,可是怎么点击,删除的事件都不响应,找了好多地方没找到原因,最后发现删除也是个按钮...

  • 深入浅出iOS事件机制

    深入浅出iOS事件机制事件传递:响应链事件传递响应链

  • 二、事件传递链和响应者链

    iOS触摸事件详解iOS开发-事件传递响应链 响应者链 UIResponser包括了各种Touch message...

  • iOS开发 - iOS14适配踩坑

    1.UITableViewCell上控件不响应点击事件 iOS14中 UITableViewCell 如果子控件是...

  • UIView响应链及事件穿透

    参考:iOS触摸事件全家桶史上最详细的iOS之事件的传递和响应机制-原理篇 1、同一层级,让上面的一层不响应,让被...

  • iOS响应者链

    参考好文 iOS开发-事件传递响应链,用运行时分析 iOS事件传递:响应者链[译] http://www.jian...

  • iOS事件的传递链和响应链

    彻底理解事件的传递链和响应链需要先弄明白iOS对象为什么可以响应用户交互,理解UIResponder类; 1.1响...

网友评论

  • 灯泡虫:在tableView中 重写 touchesBegan 并调用 [[self nextResponder] touchesBegan:touches withEvent:event]; 即可
    帅气的小时:@灯泡虫 好的,谢谢:smile:
    灯泡虫:@帅气的小时 这要看你在nextresponder中有没有做处理,一般处理不会的
    帅气的小时:你好,请问使用nextResponder对tableview的其他事件会有影响吗?
  • 低调的腹:重写tableview的touchBegan方法,为什么 在滑动的时候不走这个方法呢?
  • 京哥:这个鸡肋如何写啊:joy:
    WhisperKarl:@京哥 继承UITableView新建一个类命名为XXTableView,定义一个代理方法,在touch回调中调用代理方法,然后使用的时候直接创建XXTableView的实例,设置代理为当前控制器,在代理方法中处理touchesBegan事件即可
  • 霖溦:感谢分享,不过对于滚动视图的键盘回收,感觉还是`tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag`这样比较优雅,毕竟是系统提供的。
  • MoussyL:楼主,麻烦问一下,你那个打印hitTest:的view的分类是怎么实现的 ,能贴个demo吗? 不太明白贴出的那两个方法
    MoussyL:@WhisperKarl 好的,谢谢楼主,受教了~~~ :+1:
    WhisperKarl:@木子夕 是通过runtime交换方法实现的

    origin = class_getInstanceMethod([self class], @selector(pointInside:withEvent:));
    custom = class_getInstanceMethod([self class], @selector(kr_pointInside:withEvent:));
    method_exchangeImplementations(origin, custom);

    origin = class_getInstanceMethod([self class], @selector(hitTest:withEvent:));
    custom = class_getInstanceMethod([self class], @selector(kr_hitTest:withEvent:));
    method_exchangeImplementations(origin, custom);

本文标题:从iOS的事件响应链看TableView为什么不响应touche

本文链接:https://www.haomeiwen.com/subject/fsakettx.html