荣辱不惊,闲看庭前花开花落

嗨,我是唐顺治,(@shunzhitang) 是一名客户端iOS开发者,目前暂居北京,从事一家广告公司!!!


每一次相遇都值得珍惜 ^_^

基础篇-事件处理

对于iOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕,晃动设备,通过遥控设施控制设备。对用的事件类型有以下三种:

  • 1.触屏事件(Touch Event)
  • 2.运动事件(Motion Event)
  • 3.远端控制事件(Remote-Control Event)

一、响应者链(Responder Chain)

当发生事件响应时,必须知道谁响应事件,在iOS中,由响应链来对事件进行响应。

所有事件响应的类都是USResponder的子类,响应者链是一个由不同对象组成的层次结构,其中的每个对象将依次获得响应事件消息的机会。当发生事件时,事件首先被发送给第一响应者,第一响应者往往是事件发出的视图,也就是用户触摸的地方。事件将沿着响应链一直向下传递,知道被接受并作出处理,一般来说,第一响应者是个视图对象或者其子类对象,当其被触摸后事件被交由它处理,如果它不处理,事件就会被传递给它的视图控制器对象ViewController(如果存在),然后是它的父视图(superView)对象(如果存在),一次类推,知道顶层视图。接下来会沿着顶层视图(top view)到窗口(UIWindow对象)再到(UIApplication对象)。如果整个过程中都么有响应这个事件,该事件就被丢弃。一般情况下,在响应者链中只要有对象处理事件,事件就会停止传递。

一个典型的事件响应路线如下:

First Responser –> The Window –> The Application –> nil(丢弃)

我们通过[reponder nextResponder]找到当前responser的下一个responder,持续这个过程最后找到UIApplication对象。通常情况下,我们在First Responder(一般就是当前用户触控的view)这里就会响应请求,进入事件分发机制。

二、事件分发

第一响应者 (First responder)指的是当前接受触摸的响应者对象(通常一个UIView对象),即表示当前该对象正在与用户交互,它是响应者链的开端。响应者链和事件分发的使命都是找出第一响应者。

iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前Application 的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例UIWindow来处理,UIWindow对象首先会使用 hitTest:withEvent:方法寻找此次Touch操作初始化所在的视图(UIWindow),即需要将触摸事件传递给其处理的视图,这个过程称之为hit -test view。

// 寻找并返回最合适的view(能够响应事件的那个最合适的view)

hitTest:withEvent: 方法的处理流程入下:

  • 1.首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内。
  • 2.若返回NO,则hitTest:withEvent:返回nil,若返回YES,则向当前视图的所有子视图(subViews)发送hitTest:withEvent:消息,所有的子视图的遍历顺序是从最顶层视图一直到最底层视图,即subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。
  • 3.若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束。
  • 4.如所有子视图都返回空,则hitTest:withEvent:方法显示自身self。

三、事件处理注意和说明

1.如果最终hit-test 没有找到第一响应者,或者第一响应者没有处理该事件,则该事件会沿着响应者链向上回朔,如果UIWindow实例和UIApplication实例不能处理该事件,则该事件会被丢弃。

2.hitTest:withEvent:方法在以下情况会被忽略(不起作用):

  • 2.1 视图被隐藏,一般表现为hidden = YES
  • 2.2 禁止用户操作(userInteractionEnabled=NO) 的视图
  • 2.3 alpha级别小于0.01(一般0.0-<0.01表示透明)
  • 2.4 如果一个子视图的区域超过父视图的bound区域(父视图的clipsToBounds属性为NO,这样超过父视图的bound区域子视图也会显示),那么正常情况下对于子视图在父视图之外的区域触摸是不会被识别的,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。当然我们可以重写pointInside:withEvent:方法来处理这种情况。

3.可以通过重写hitTest:withEvent 来达到有些特定的需求。可以参考CYLTabBarController是一个支持自定义 Tab 控件的开源项目。实现了超出TabBar frame也可以响应。

总结:
当手指触摸(touch)操作时,iOS会将其打包成一个UIEvent对象,并加入Application的时间队列,touch事件就会沿着(UIApplication -> UIWindow ->view)去寻找最合适响应的view,如果当前的view不响应这个事件 ,则需要按照响应链(responser ->UIWindow ->UIApplication ->nil)去找到响应对象,实现这个事件的响应。如果到最后没有响应就丢弃。


参考资料:

@iOSGit资料

最近的文章

2017新的开始

前前后后折腾了很多的博客,但是都感觉到最后没有了毅力和精力去打理,一直想自己搭建一个网站自己写属于自己的博客,但是都由于种种原因一直搁浅,在2017年来临之际,我决定自己要重新开始了,做过了三年的iOS开发,开发项目,写代码,很多的知识点都写在印象笔记或者别的地方,有时间找也找不到,新的开始,从这里起航…2016年马上就要过年了,北漂的日子满满的一年半多了,这一年找到工作,上了班,正式的开始我的北漂生活,这一年有辛酸也有快乐,对于做技术的我来说北京确实是一个牛人居多的地方,这里的生活方...…

随记杂谈集继续阅读
更早的文章

基础篇-网络编程

现在的APP大多数情况都是需要网络进行操作的,通过网络,一款应用才能够内容丰富,才能够完成用户操作和服务器后台数据数据的交互,网络编程是任何程序开发中不可缺少的部分,当然在iOS客户端也显得尤为重要。一、网络编程Cocoa中网络编程层次结构分为三层,自上而下分别是: Cocoa层: NSURL,Bonjour,Game Kit ,WebKit Core Foundation层: 基于C的CFNetwork和CFNetServices OS层: 基于C的BSD so...…

Objective-C回顾温习集继续阅读