iOS事件传递

iOS中事件分为

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

响应者对象

能够接收并处理事件的对象,必须是UIResponder的子类。比如:UIApplicationUIWindowUIView等。

UIResponder处理事件的几种方法

1、触摸事件

// 一根或者多根手指开始触摸
- (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;
// 预估触摸特性,当从边缘进入,系统不知道当前触摸特性,会被调用下列方法
- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches;

2、加速计事件

// 开始动起来
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
// 运动结束
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
// 运动被打断,取消
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;

3、远程控制事件

- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

UITouch

  • 触摸事件中,touches中存放的是UITouch对象,一根手指对应一个UITouch对象
  • 触摸开始,系统会创建手指对应的UITouch对象,随着手指移动,UITouch对象会随着更新,触摸事件结束,系统会销毁这个UITouch对象
1、属性
// 触摸产生时对应触摸的窗口
@property(nullable,nonatomic,readonly,strong) UIWindow *window;
// 触摸产生时对应的触摸视图
@property(nullable,nonatomic,readonly,strong) UIView *view;
// 触摸事件产生或者变化的时间(单位秒)
@property(nonatomic,readonly) NSTimeInterval timestamp;
// 当前触摸事件的状态(开始:Began、移动:Moved、静止:Stationary、结束:Ended、取消Cancelled)
@property(nonatomic,readonly) UITouchPhase phase;
// 短时间的触摸次数,据此判断是几根手指触摸,双击、单击或者其他更多
@property(nonatomic,readonly) NSUInteger tapCount;
// 触摸手势组
@property(nullable,nonatomic,readonly,copy)   NSArray <UIGestureRecognizer *> *gestureRecognizers;
# pragma mark - iOS8新增属性
// 触摸的主半径
@property(nonatomic,readonly) CGFloat majorRadius;
// 触摸主半径的公差
@property(nonatomic,readonly) CGFloat majorRadiusTolerance;
# pragma mark - iOS9新增属性
// 触摸类型(直接手指触摸:Direct、间接触摸:Indirect、触摸笔触摸:Stylus)
@property(nonatomic,readonly) UITouchType type;
// 触摸力度,其中1.0表示平均力压值
@property(nonatomic,readonly) CGFloat force;
// 能触摸的最大力度
@property(nonatomic,readonly) CGFloat maximumPossibleForce;
// 高角度,仅适用于用触摸笔触摸类型。0:触摸笔平行于平面;M_PI/2:触摸笔垂直于平面
@property(nonatomic,readonly) CGFloat altitudeAngle;
// 预估更新索引,可以让更新的触摸对象与原对象进行关联,当前触摸对象的触摸特性变化,该值就会加一
@property(nonatomic,readonly) NSNumber * _Nullable estimationUpdateIndex;
// 预估属性,当前触摸对象的预估触摸特性(力度:Force、方位角:Azimuth、高度:Altitude、位置:Location)
@property(nonatomic,readonly) UITouchProperties estimatedProperties;
// 预期将要更新的特性,如果没有更新,则当前值为最终预估值,用于从边缘进入时系统开始无法获得准确的touches值
@property(nonatomic,readonly) UITouchProperties estimatedPropertiesExpectingUpdates;
2、方法
// 返回触摸在view上的坐标位置(以view左上角原点(0, 0)为准),若传入的view = nil,则返回触摸点在window上的位置
- (CGPoint)locationInView:(nullable UIView *)view;
// 同上,这个方法记录了上一个触摸点的位置
- (CGPoint)previousLocationInView:(nullable UIView *)view;
# pragma mark - iOS9新增方法
// 精准的返回触摸在view上的坐标位置
- (CGPoint)preciseLocationInView:(nullable UIView *)view;
// 精准的返回上一个在view上的触摸位置
- (CGPoint)precisePreviousLocationInView:(nullable UIView *)view;
// 返回触摸在view上的方位角(仅适用于触摸笔触摸事件)
- (CGFloat)azimuthAngleInView:(nullable UIView *)view;
// 返回触摸在view上的指向方向角的单位向量(仅适用于触摸笔触摸事件)
- (CGVector)azimuthUnitVectorInView:(nullable UIView *)view;

UIEvent

  • 每产生一个事件就会产生一个UIEvent对象
  • UIEvent对象记录了事件的产生时刻和类型
1、属性
// 事件类型(Touches:点击、Motion:加速计、RemoteControl:远程控制、Presses:按压)
@property(nonatomic,readonly) UIEventType type;
/**
同一事件类型的具体事件

None:type是触摸
MotionShake:type是加速计

== 剩下是针对远程控制事件类型 ==
RemoteControlPlay:播放
RemoteControlPause:暂停
RemoteControlStop:停止
RemoteControlTogglePlayPause:切换播放暂停
RemoteControlNextTrack:下一曲
RemoteControlPreviousTrack:上一曲
RemoteControlBeginSeekingBackward:开始往后寻找(开始快退)
RemoteControlEndSeekingBackward:结束向后寻找(结束快退)
RemoteControlBeginSeekingForward:开始向前寻找(开始快进)
RemoteControlEndSeekingForward:结束向前寻找(结束快进)
*/ 
@property(nonatomic,readonly) UIEventSubtype subtype;
// 事件产生的时间
@property(nonatomic,readonly) NSTimeInterval timestamp;
// 事件中产生的所有触摸点
@property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches;
2、方法
// 返回在窗口上的所有触摸点
- (nullable NSSet <UITouch *> *)touchesForWindow:(UIWindow *)window;
// 返回在当前视图上的所有触摸点
- (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view;
// 返回事件中手势的触摸点
- (nullable NSSet <UITouch *> *)touchesForGestureRecognizer:(UIGestureRecognizer *)gesture
# pragma mark - iOS9新增方法
// 将丢失的触摸点放在一个数组中返回(手机扫描渲染是每秒60帧,会出现绘画延迟,所以有些触摸点会丢失,下列方法是针对这种情况的优化)
- (nullable NSArray <UITouch *> *)coalescedTouchesForTouch:(UITouch *)touch;
// 预测的一组触摸点位置,但是不定和实际触摸点一样,所以是预估值
- (nullable NSArray <UITouch *> *)predictedTouchesForTouch:(UITouch *)touch;

不能接收事件情况:

  • 不能接收用户交互,self.userInteractionEnabled = NO
  • 被隐藏,self.hidden = YES
  • 透明度小于0.01,self.alpha < 0.01
  • 如果父控件不能接收触摸事件,那么子控件也将不能接收

触摸事件中事件的产生和传递

1、事件发生后,系统会将事件加入到UIApplication管理的队列中
2、UIApplication遵循先进先出的原则,将队列中先进的事件发送给主窗口(keyWindow)
3、主窗口会在视图层级中找到一个合适的view来接收和处理事件
4、找到合适的view后,会调用视图的touches方法来具体处理事件

具体寻找合适的view

1、先判断view是否能接收触摸事件
2、触摸点是否在view身上
3、在子控件数组中从后向前遍历子控件,然后重复上面两步,找到则返回合适的子控件
4、没找到合适的子控件,则自己就是合适处理的

选出最佳view方法

// 事件一传递给控件,就会调用下列方法,返回最合适接收事件的UIView
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
// 默认值是 YES,返回触摸点是否在调用者身上
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;

事件传递详细过程

1、触摸事件产生和传递后,找到最佳接收和处理事件的view
2、插入说明:响应者链条由事件响应者组成
3、如果最佳视图调用了[super touches...],则事件由下往上顺着响应者链传递上去
4、接着就会调用上一个响应者的touches...方法
5、如果上一个响应者不能响应,则传递给上上一个响应者
6、如果传递到UIApplication还是不能处理,那这个时间就会被废弃

判断上一个响应者

  • 如果当前视图是视图控制器的view,则上一个响应者是该视图的视图控制器
  • 如果当前视图没有视图控制器,则上一个响应者是该视图的父视图

实例

实例图片

所属关系:
self.view:子控件灰色
灰色:子控件先添加红色,后添加绿色
绿色:子控件先添加橘色,后添加白色

符合:满足可接收事件条件,自身能接收事件,触摸点在自己身上
否:不能接收事件

点击红色:UIApplication -> UIWindow -> 灰色(符合)-> 查询灰色子控件(红色、绿色)-> 绿色(触摸点不在身上,否)-> 红色(符合)
点击白色:UIApplication -> UIWindow -> 灰色(符合)-> 查询灰色子控件(红色、绿色)-> 绿色(符合)-> 查询绿色子控件(白色、橘色)-> 橘色(触摸点不在身上,否)-> 白色(符合)
点击橘色:UIApplication -> UIWindow -> 灰色(符合)-> 查询灰色子控件(红色、绿色)-> 绿色(符合)-> 查询绿色子控件(白色、橘色)-> 橘色(符合)

具体演示demo,请点击这里

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

推荐阅读更多精彩内容