iOS的事件是一个由触发行为到响应的过程。本文旨在表达事件如何处理响应,如何传递事件的。
1. 前言
国内智能手机要从2000年开始说起,手机的进化是飞速的,单从操作来看,最开始是数字实体键盘,后来出现了全字母的实体键盘。2007年首台iPhone诞生让我们看到了新的操作方式,它只有一个按键。2008年诺基亚5800作为第一款全电阻屏膜触摸手机,同年魅族M8全电容屏触摸手机紧随其后。至此手机操作已完全脱离实体键盘并且可以多点触控。言归正传本文不去探索电容屏的多点触控功能和原理,只剖析一下iPhone是如何通过人的一根手指触摸到最终响应的过程。本文以触摸事件为例。
iOS开发中常见的事件有以下4种(※不包含3Dtouch):
2. 事件相关类
UITouch:
定义:保存着跟手指相关的信息,比如触摸的位置、时间、阶段、大小、运动、力、角度等,一根手指只对应一个UITouch对象。
生命周期:当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置,当手指离开屏幕时,系统会销毁相应的UITouch对象。
UIEvent:
定义:事件对象,记录事件产生的时刻和类型,每产生一个事件,就会产生一个UIEvent对象。
UIResponder:
定义:响应者对象,用来响应用户的操作,处理各种事件。
封装了UIEvent、UITouch、UIPress、UIMenuBuilder等事件相关类,目的为其子类提供一系列方法可以重写处理,并可以用来获取响应的状态及相关属性。四个常用的touch方法就在这个类里定义的:
// 一根或多根手指开始触摸屏幕
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 一根或多根手指在屏幕上移动,注意此方法在移动过程中会重复调用
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 一根或多根手指触摸结束离开屏幕
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 触摸意外取消(例如正在触摸时打入电话)
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
UIGestureRecognizer:
它实现了上面的四个touche方法,但是它不是一个UIResponder子类,因此它并不在响应链中。
UITouch 和 UIEvent 提供了一些方法来获取触摸和手势识别的关联性。UITouch的gestureRecognizers 会列举出当前处理这个touch的所有手势。
touchesForGestureRecognizer: 方法列举了由指定手势识别所处理的触摸。
3. 两个函数
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
这2个函数定义在UIView中,因此只有UIView及其子类才可以重写这2个方法,通过遍历调用视图栈中每个视图的这两个函数来查找第一响应者。接下来分别来说一下他们的作用。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
该方法会被系统调用(可重写),其主要作用是查找命中视图:在视图的层次结构中寻找到一个最适合的视图 (理论为最上层视图)来响应触摸事件,如果返回为nil,即事件有可能被丢弃。此过程也就是查找第一响应者的过程。
- (BOOL)pointInside:(CGPoint)point withEve