iOS学习9_事件分发&响应链

原创 2015年07月08日 20:23:51

iOS的三种事件:触摸事件/运动事件/远程控制事件

typedef enum {
UIEventTypeTouches,
UIEventTypeMotion,
UIEventTypeRemoteControl,
} UIEventType;

只有继承UIResponder类的对象才能处理事件,如UIView、UIViewController、UIApplication都继承自UIResponder,都能接收并处理事件。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;


事件分发:Hit-Testing View

当用户触摸了设备的屏幕,iOS会识别这一系列触摸事件并且封装到UIEvent对象里面,并且放到application的事件队列,在处理事件时,UIApplication会从事件队列取出最前面的事件并分发处理,一般他会发送给主窗口,接下来主窗口将该事件传递到an initial object来处理,对于触摸事件来说该对象是Touch events


首先会将该事件发送给最合适的对象来处理。对于触摸事件该对象指的是hit-test view,对于其他事件,该对象指的是第一响应者。

UIKit first sends the event to the object that is best suited to handle the event. For touch events, that object is the hit-test view, and for other events, that object is the first responder.

Hit-Testing 指的是找到当前触摸事件在哪个View上发生的过程:如果触摸在一个View的边界里面,就会递归的检查它的子View是不是包含了触摸点。最外面的包含了该触摸点的View就称为hit-test view。iOS确定了hit-test view后就会将触摸事件传递给该View来处理。

hitTest:withEvent:具体的实现:

hitTest:withEvent:方法首先会调用pointInside:withEvent:方法,如果触摸点在View的内部,该方法返回YES。接下来会递归的调用所有pointInside:withEvent:方法返回YES的子view的hitTest:withEvent:方法。

如果触摸点不在View里面,调用pointInside:withEvent:方法会返回NO,hitTest:withEvent:会返回nil。如果子view在调用pointInside:withEvent:时返回NO,那么以这个子view为父视图的所有子view都不必再检查。也就是说如果触摸点没在一个子view上,那肯定也不可能在该子view的子view上面。也就是说在父view外面的任何子view的部分永远不可能接收到触摸事件。


当触摸最上面绿色控件的红色框框的那部分永远无法接收到触摸事件。这种情况在view的clipsToBounds属性设置为NO时会出现。

下面的例子展示了hitTest:withEvent:函数的过程


假设用户触摸了图中的view E。iOS通过如下顺序查找hit-test view

1.触摸点在A里面,因此检测子view B和C

2.触摸点不在B里面,但是在C里面。因此检测C的子View D和E。

3.触摸点不在D里面,但是在E里面,并且E是在最外层的包含触摸点的view,因此E就是要找的hit-test view

hitTest:withEvent:函数的实现代码:

1.能否自己处理?不能,return nil;

2.点在不在当前控件上?没在,return nil;

3.说明能处理触摸事件,并且在当前控件上,是合适的控件,但不一定是最合适的。从后往前遍历自己的子控件,是否是最合适的控件(包含该触摸点的View)。如果是,返回该View。

4.说明没找到比自己合适的View,返回自己。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event的实现:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01)
    {
        return nil;
    }
    if (![self pointInside:point withEvent:event])
    {
        return nil;
    }
    __block UIView *hitView = self;
    [self.subViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {   

        CGPoint thePoint = [self convertPoint:point toView:obj];

        UIView *theSubHitView = [obj hitTest:thePoint withEvent:event];

        if (theSubHitView != nil)
        {
            hitView = theSubHitView;

            *stop = YES;
        }

    }];

    return hitView;
}


响应链

响应链是一系列响应对象,响应链中,所有对象的基类都是UIResponder从firstResponse开始到application对象结束。firstResponse意味着首先接收到事件,一般第一响应者是一个view,对于触摸事件来说就是hit-test view。

前面说过,UIKit首先会将该事件发送给最合适的对象来处理。对于触摸事件该对象指的是hit-test view,对于其他事件,该对象指的是第一响应者。

但是实际上响应链也用在触摸事件/运动事件/远程控制事件/等

如果hit-test view无法处理一个事件,事件就通过响应链往上传递(hitTestView算是第一个Responder),直到找到一个可以处理的Responder为止。

下图给出了沿着响应链传递的顺序。两个图的区别是视图的层次关系不一样。响应链从firstResponse开始接下来是它的父视图,如果没有父视图直到它的控制器(如果有的话)再到window和application。


initial object可能是hit-test view或者是first responder,没有处理事件。UIkit就会将该事件传递给next responder下一个响应者,每个响应者通过调用

-nextResponder方法决定是处理该事件还是向响应链的上层传递,直到某个响应者处理了该事件或者没有响应者了为止。

不建议直接给nextResponder发消息即:

[self.nextResponder touchesBegan:touches withEvent:event];

而建议调用父类的实现,让UIKit来帮我们做,因为默认的实现是将事件沿着响应链继续向上传递到下一个responder。

[super touchesBegan:touches withEvent:event];

注:

刚开始看到hit-test view和响应链的概念有点弄混了,直到把文档好好看了一下才弄明白:当一个事件发生需要处理时,会让合适的对象去处理。如果是触摸事件的话,该对象就是hit-test view。如果是其他事件,该对象指的就是第一响应者(响应链中)。响应链是一个比较大的范畴,在触摸事件中,hit-test view就是响应链中的第一响应者。也就是说在触摸事件中通过hitTest:withEvent:方法找到的hit-test view就是第一响应者。


Android中的事件分发

回过头简单整理了一下android里面事件传递的过程: Android之View和ViewGroup事件分发

ViewGroup传递的时候经历了dispatchTouchEvent--->onInterceptTouchEvent

从后往前遍历子View,如果子View可以处理事件返回true。传递到此结束

如果子View不能处理该事件,自己处理....onTouchEvent

View处理事件

dispatchTouchEvent—> onTouch –-> onTouchEvent

设置监听onTouch返回了true  或者返回了false但是onTouchEvent返回true都可以认为子View成功处理了事件

若返回了false,事件会往上抛,那么该ViewGroup会调用自己的onTouchEvent处理事件。


在iOS里面事件会因为透明度为0而不能处理/传递事件,在android里面却可以。

iOS里面如果父控件设置不能交互那么上面的子空间也不能获得事件,而android里面即使服布局设置enable为false或者clickable设置为false,上面的子空间均可接收到事件。

iOS里面没有view和view容器之分,而android里面有View和ViewGroup之分,所以事件分发也稍麻烦,

等...


感想:

学习iOS一个月,发现上手是挺快的,好多东西和android是类似的,学起来非常快,但是一直困扰我的就是每当在学iOS里面新的东西的时候我总会想之前android里面是怎么实现的,两者的差别。I am so puzzled also very interested ^_^

iOS的事件分发

移动平台
  • sakulafly
  • sakulafly
  • 2014年01月25日 20:29
  • 8046

iOS事件的分发机制和响应者链(Swift)

当我们在设计自己的APP时,可能会想动态的响应事件.例如:屏幕上许多对象都能够发生触摸,我们必须决定哪一个对象来响应给定的事件并且知道对象是如何接受事件的。当用户事件产生的时候,UIKit会创建一个事...
  • longshihua
  • longshihua
  • 2016年07月19日 10:48
  • 2669

iOS 事件响应链详解(The Responder Chain)

原创Blog,转载请注明出处 http://blog.csdn.net/hello_hwc?viewmode=list 我的stackoverflow前言:在iOS编程中,经常会有复杂的时间v...
  • Hello_Hwc
  • Hello_Hwc
  • 2015年10月16日 12:17
  • 4109

iOS 事件传递之响应链

当你设计App时你可能需要动态的响应事件。例如,一个触摸事件可能发生在屏幕上不同的对象中,你需要决定哪个对象来响应这个给定的事件,理解对象如何接收事件。 当用户触发的一个事件发生,UIKit会创...
  • l863784757
  • l863784757
  • 2016年03月14日 12:06
  • 745

iOS事件处理---响应者链

一、定义(Responder Chain)   响应者链就是由一系列的响应者对象(响应和处理事件能力的对象)构成的一个层次结构 (或者链条)。它的作用是能让多个控件处理同一个触摸事件。 第一响应者...
  • jasonjwl
  • jasonjwl
  • 2016年03月21日 14:39
  • 605

【iOS开发】事件处理之响应链(二)

事件传递:响应链当你设计你的应用程序,很可能想要得到事件的动态响应。例如,可能会出现一个触摸,有多个对象响应,因此你必须要决定是哪个对象要响应和处理此触摸事件。当用户生成的事件发生时,UIKit中创建...
  • Hanrovey
  • Hanrovey
  • 2016年06月08日 16:42
  • 1487

oc-事件传递响应链

大概的逻辑如下- - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ UIView *touchView = se...
  • li198847
  • li198847
  • 2016年05月17日 20:25
  • 1305

iOS开发 - 事件传递响应链

一、序言当我们在使用微信等工具,点击扫一扫,就能打开二维码扫描视图。在我们点击屏幕的时候,iphone OS获取到了用户进行了“单击”这一行为,操作系统把包含这些点击事件的信息包装成UITouch和U...
  • xiaoxiaobukuang
  • xiaoxiaobukuang
  • 2017年02月13日 11:42
  • 738

iOS- 响应者链, runtime, runloop

响应者链 响应者链条概念:iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,单例的UIApplication会从事件队...
  • zhz459880251
  • zhz459880251
  • 2016年04月04日 23:51
  • 1209

事件分发和响应链

#import @interface View : UIView //颜色名字 @property (nonatomic, copy) NSString *colorName; @end #i...
  • u012701023
  • u012701023
  • 2016年01月10日 22:40
  • 228
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:iOS学习9_事件分发&响应链
举报原因:
原因补充:

(最多只允许输入30个字)