Detailed explanation of the iOS event response chain (The Responder Chain)

Foreword: In iOS programming, there are often complex time view nesting, such as complex views nested in uitableviewcell. At this time, the responder of the touch event is very important. In this basic article written before , I briefly explained the types of events in iOS. This article focuses on touch as an example to explain the transfer of touch.


touch event responder

The window object will always try to set the responder to the view generated by touch, judged by hit-test. The Touch event will be delivered to the UI Application along the first responder (Fist Responder), and if it fails to respond at the end, it will be discarded.

How does the system find the Fist Responder's View through hit-test? 
For example 
, click on viewD as shown in the figure and 
 
use hit-test to determine the order of touches in that view as follows

  1. Touch is in the bounds of ViewA, recursively checks ViewB and ViewC
  2. Touch is not in the bounds of ViewB, check ViewC
  3. Touch is in the bounds of ViewC, check ViewD and ViewE
  4. Touch is not in ViewD, check ViewE
  5. Touch is in ViewE, so ViewE is the result of hit-test

Each hit-test is implemented by two functions. 
Call pointInside:withEvent: to return whether the touch point is in the View, hitTest:withEvent: to return the View where the touch point is located, and then recursively check the subview

Therefore, you can limit a part of a View to respond to the view by overriding pointInside:withEvent. About this, I will write an example at the end.


Delivery order of Touch events

注意,只有UIResponer的子类可以处理Touch事件,包括 UIApplication, UIViewController,和 UIView及其子类。

这里写图片描述

  1. 首先由View尝试处理,不能处理,则传递给Superview
  2. SuperView不能处理,继续传递给SuperView
  3. 因为SuperView是ViewController的根视图,则传递给ViewController
  4. ViewController不能处理,重复1,2,直到到达UIWindow
  5. Window不能处理,传递给UIApplication
  6. 抛弃

重写PointInside的一个例子

在右下角加一个不规则Button,注意,不能只设置背景色或者mask,因为button是矩形,button的响应区域也是矩形。这时候重写pointInside就有用了。 

代码 
为了方便,使用了hard-code 数字

#import "CustomButton.h"

@interface CustomButton ()

@property (strong,nonatomic)CAShapeLayer * shapeLayer;

@end
@implementation CustomButton

-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        [self setUp];
    }
    return self;
}
-(void)setUp{
    self.shapeLayer = [CAShapeLayer layer];
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil,100, 0);
    CGPathAddLineToPoint(path, nil,100,100);
    CGPathAddLineToPoint(path, nil,0, 100);
    self.shapeLayer.path = path;
    [self.layer setMask:self.shapeLayer];
    self.layer.masksToBounds = true;
    self.backgroundColor = [UIColor lightGrayColor];
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    if (CGPathContainsPoint(self.shapeLayer.path, nil, point, true)) {
        return [super pointInside:point withEvent:event];
    }else{
        return false;
    }
}

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

前言:在iOS编程中,经常会有复杂的时间view嵌套,例如uitableviewcell中嵌套复杂的视图。这时候的touch事件的响应者就十分重要。在这篇之前写的基础文章中,我简单讲解了iOS中的事件种类,本文侧重以touch为例,讲解touch的传递。


触摸事件的响应者

window对象总会尝试把响应者设为touch产生的view,通过hit-test来判断。Touch事件会沿着第一响应者(Fist Responder)一直传递到UI Application,如果到最后也不能响应,则被抛弃。

系统如何通过hit-test找到Fist Responder的View 
举个例子 
点击如图的viewD 
 
通过hit-test来判断触摸在那个view的顺序如下

  1. Touch在ViewA的bounds中,递归检查ViewB和ViewC
  2. Touch不在ViewB的bounds中,检查ViewC
  3. Touch在ViewC的bounds中,检查ViewD和ViewE
  4. Touch不在ViewD中,检查ViewE
  5. Touch在ViewE中,所以ViewE为hit-test的结果

每一次hit-test通过两个函数实现。 
调用pointInside:withEvent: 返回改触摸点是否在View中,hitTest:withEvent:返回触摸点所在的View,然后递归检查起subview

所以,可以通过重写pointInside:withEvent来限制一个View的部分区域响应视图,关于这个,最后我会写个例子。


Touch事件的传递顺序

注意,只有UIResponer的子类可以处理Touch事件,包括 UIApplication, UIViewController,和 UIView及其子类。

这里写图片描述

  1. 首先由View尝试处理,不能处理,则传递给Superview
  2. SuperView不能处理,继续传递给SuperView
  3. 因为SuperView是ViewController的根视图,则传递给ViewController
  4. ViewController不能处理,重复1,2,直到到达UIWindow
  5. Window不能处理,传递给UIApplication
  6. 抛弃

重写PointInside的一个例子

在右下角加一个不规则Button,注意,不能只设置背景色或者mask,因为button是矩形,button的响应区域也是矩形。这时候重写pointInside就有用了。 

代码 
为了方便,使用了hard-code 数字

#import "CustomButton.h"

@interface CustomButton ()

@property (strong,nonatomic)CAShapeLayer * shapeLayer;

@end
@implementation CustomButton

-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        [self setUp];
    }
    return self;
}
-(void)setUp{
    self.shapeLayer = [CAShapeLayer layer];
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil,100, 0);
    CGPathAddLineToPoint(path, nil,100,100);
    CGPathAddLineToPoint(path, nil,0, 100);
    self.shapeLayer.path = path;
    [self.layer setMask:self.shapeLayer];
    self.layer.masksToBounds = true;
    self.backgroundColor = [UIColor lightGrayColor];
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    if (CGPathContainsPoint(self.shapeLayer.path, nil, point, true)) {
        return [super pointInside:point withEvent:event];
    }else{
        return false;
    }
}

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325590131&siteId=291194637