iOS---事件传递和响应机制

iOS 中的事件

  • 触摸事件
  • 加速计事件
  • 远程控制事件

响应者对象(UIResponder)

只有继承 UIResponder 的对象才能响应事件

  • UIApplication
  • UIViewControl
  • UIView
UIResponder内部提供了以下方法来处理事件触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;



touchesBegan 这个方法,如果是两个手指同时触摸,这个方法只会调用一次,方法中包含两个 UITouch 的对象 , 如果是一前一后的触摸,就会调用两次这个方法,方法中包含一个 UITouch 对象

注意:
1.想要处理 UIView 的触摸事件,就要继承 UIView ,然后重写 UIView 的触摸事件方法
2.UIViewControl 在其触摸事件方法中就可以处理

UITouch 对象

UITouch 的属性

触摸产生时所处的窗口
@property(nonatomic,readonly,retain) UIWindow *window;

触摸产生时所处的视图
@property(nonatomic,readonly,retain) UIView *view
;

短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger tapCount;

记录了触摸事件产生或变化时的时间,单位是秒@property(nonatomic,readonly) NSTimeInterval timestamp;

当前触摸事件所处的状态
@property(nonatomic,readonly) UITouchPhase phase;


UITouch 的方法

(CGPoint)locationInView:(UIView *)view;
// 返回值表示触摸在view上的位置
// 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
// 调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置

(CGPoint)previousLocationInView:(UIView *)view;
// 该方法记录了前一个触摸点的位置


  • 当用户用一根手指触摸屏膜时,就会创建一个 UITouch 对象
  • 一根手指对应一个 UITouch 对象
  • 当手指移动时,系统会更新 UITouch 对象的位置等信息
  • 当手指离开时,相应的 UITouch 对象就会销毁


IOS 的事件产生和传递

事件产生

  1. 发生触摸事件后,系统会把事件加入到由 UIApplication 管理的事件队列
  2. UIApplication 会从事件队列中取出最前面的事件,并将事件分发下去处理,通常是先发给应用程序的KeyWindow
  3. KeyWindow 会找其视图层结构找到一个最适合的View 来处理事件


事件的传递

UIApplication -> Window -> 寻找最合适的View

如何寻找最合适的 View?

  1. 首先判断 KeyWind 是否能接受触摸事件
  2. 判断触摸点是否在窗口身上
  3. 从后往前遍历子控件,重复上述两个步骤
  4. 遍历到最合适的 View
  5. 如果没有最合适的 View ,那么就由 KeyWind 处理



⚠️注意点:

  1. 透明度 < 0.01 的控件,是不能接受触摸事件
  2. 如果父控件不能接收事件,那么子控件也不能
  3. 不允许交互:userInteractionEnabled = NO
  4. 不管控件能否接收触摸事件,主要有触摸,就会产生事件,只是这个事件会不会被处理

如何寻找最合适的 View 的底层揭秘

hitTest:withEvent:
pointInside

hitTest:withEvent:方法

  • 调用:主要有事件传递给一个控件,控件就会调用该方法
  • 作用:找到最合适的 View ,并返回该 View

拦截事件的处理

  • 正因为hitTest:withEvent:方法可以返回最合适的view,所以可以通过重写hitTest:withEvent:方法,返回指定的view作为最合适的view。
  • 不管点击哪里,最合适的view都是hitTest:withEvent:方法中返回的那个view。
  • 通过重写hitTest:withEvent:,就可以拦截事件的传递过程,想让谁处理事件谁就处理事件。


如果想指定 View 作为最适合的 View,有以下两种方法

  1. 在父控件的hitTest:withEvent:返回子控件
  2. 在子控件的hitTest:withEvent:返回自

⚠️注意点:
推荐方法一,不推荐方法二的原因是有可能不能成功返回子控件,如果触摸点不在子控件,而在另一个子控件,那么没办法返回该子控件。

事件传递的正真顺序

  1. 产生触摸事件
  2. UIApplication 事件队列
  3. [UIWindow hitTest:withEvent:];
  4. 返回更合适的 View
  5. [子控件 hitTest:withEvent:];
  6. 返回更合适的 View


#import "WSWindow.h"
@implementation WSWindow
// 什么时候调用:只要事件一传递给一个控件,那么这个控件就会调用自己的这个方法
// 作用:寻找并返回最合适的view
// UIApplication -> [UIWindow hitTest:withEvent:]寻找最合适的view告诉系统
// point:当前手指触摸的点
// point:是方法调用者坐标系上的点
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    // 1.判断下窗口能否接收事件
     if (self.userInteractionEnabled == NO || self.hidden == YES ||  self.alpha = 0; i--)     { 
    // 获取子控件
    UIView *childView = self.subviews[i]; 
    // 坐标系的转换,把窗口上的点转换为子控件上的点 
    // 把自己控件上的点转换成子控件上的点 
    CGPoint childP = [self convertPoint:point toView:childView]; 
    UIView *fitView = [childView hitTest:childP withEvent:event]; 
    if (fitView) {
    // 如果能找到最合适的view 
    return fitView; 
    }
    } 
    // 4.没有找到更合适的view,也就是没有比自己更合适的view 
    return self;
    }
    // 作用:判断下传入过来的点在不在方法调用者的坐标系上
    // point:是方法调用者坐标系上的点
    //- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    //{
    // return NO;
    //}
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    NSLog(@"%s",__func__);
    }
    @end


pointInside:withEvent:方法

pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件


事件响应

响应者链条:是由多个响应者连接起来的链条。

事件响应的流程

1.找到最合适的 View 的时候,就会调用自己 touchs 的方法处理事件。
2.touches默认做法是把事件顺着响应者链条向上抛.
2.1 首先看 initail View 能不能处理事件
2.2 不能就把事件传递给父控件,继续判断
2.3 没有一个控件能处理就抛给 Window
2.4 Window 处理不了,最后抛给 UIApplication
2.5 UIApplication 处理不了,把事件丢弃。
如果以上一个响应者重写了 touches 方法,就能处理事件

如何做到一个事件多个对象处理
因为系统默认做法是把事件上抛给父控件,所以可以通过重写自己的touches方法和父控件的touches方法来达到一个事件多个对象处理的目的。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
// 1.自己先处理事件...
NSLog(@"do somthing...");
// 2.再调用系统的默认做法,再把事件交给上一个响应者处理
[super touchesBegan:touches withEvent:event]; 
}


事件传递和事件响应的过程总结

  1. 事件产生,添加到 UIApplication 的事件队列
  2. UIApplication 把事件队列最前的事件传递给UIWindow
  3. UIWindow 调用 hitTest:withEvent: 返回一个合适的 View
  4. View 继续调用 hitTest:withEvent: 找到一个最适合的 View
  5. 事件传递给最适合的 View 就开始响应事件
  6. 如果最适合 View 不能处理事件,把事件抛给父控件
  7. 一直都没有控件可以处理事件,就把事件抛给 UIWindow
  8. UIWindow 处理不了就抛给 UIApplication
  9. 最后处理不了就把事件抛弃。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容