iOS的响应链的理解
作为iOS开发者,相应链大家理解的每个人都不太一样,下面陈述下我理解的iOS相应链机制是什么样的?可能对,也可能不对,不对的还望理解更透彻的人给予指点(感谢)!
首先说下为什么会有相应链这个东西,顾名思义,响应链,肯定是响应你行为操作的一个次序链条吧!响应链我理解的其实是两个步骤
1、 首先当你手触摸到屏幕的时候,iOS系统会产生一个需要处理的事件,这个事件就是被封装成UIEvent对象,然后放到系统的任务队列里边去处理,传递,查找出当前手是触目的那个UIView,这个过程,是响应链的事件传递,也是视图的查找,查找是从视图的最底层也就是视图最下边的父视图开始依次向上传递查找, 这里要说的查找的时候要说的两个方法,第一个方法作用是返回最上层的试图,也就是子孙试图中,辈分最小的那个,第二个方法是判断当前点在不在改视图接受着也就是bounds的区域内,不在的话,该视图对应的hitTest方法返回肯定是空,也就不会再去遍历PointInside 返回为NO的视图上的子试图了
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
首先结合下边这个视图来介绍下这两个方法
举个例子比如
我现在分别在自定义的 A、B、C、D、E这几个自定义的UIView 里边重写了
- -(nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)even
-
(BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event
这两个方法 然后看他们的打印情况
我现在就举中一个例子
当你手点击E视图的时候 首先我们来先看下打印结果吧:
image.png
-
结果是AHitese --->APoint --->CHitest --->CPoint ->EHitTest ---->Epoint 然后找到了E才执行了E的、C、A的hitest的返回View
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event响应方法
这里方法打印了两次应该是我在重写放的内部调用 [super _cmd] 对应的方法吧,具体愿意不知道 猜想的,但是顺序我们知道了 然后再点击A的结果和B的结果,分别得到的经验就是遍历view上的所有子试图,但是他们有层级结构,比如这里的A是父视图,B、C、D是同级的A的子试图,E视图是最底层C的子试图,所以我从点击打印的结果得到的结论是 如果你点击了A
那么他们调用各自的HitTest的方法顺是 A最先,然后再调用A下边的子试图,但是A的子试图也是有优先级别的,这调用的级别规律是符合出栈入栈规律的,相同级别情况下,后添加的先调用的规律
image.png
也就是说调用hitTest 无论返回的UIView是否存在,都再会调用PointInside方法的,然后通过PointInside方法返回的YES和NO再去判断用不用调用子试图的HitTest方法,如果返回YES调用,recursively(系统APi说了)递归调用子试图的Hittest方法,如果返回NO就不在调用子试图的HitTest方法 也就不在调用poinsinside方法,
HitTestGif.gif#import "CustomView.h"
#import "CustomBtn.h"
@interface CustomView()
@property (strong, nonatomic) IBOutlet UIView *contentView;
@property(nonatomic,strong)CustomBtn *customBtn;
@end
@implementation CustomView
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.customBtn = [[CustomBtn alloc] initWithFrame:CGRectMake(50, -50, 100, 100)];
_customBtn.backgroundColor = [UIColor redColor];
[self addSubview:self.customBtn];
[_customBtn addTarget:self action:@selector(isinAction) forControlEvents:(UIControlEventTouchUpInside)];
_customBtn.bounds = UIEdgeInsetsInsetRect(self.customBtn.bounds, UIEdgeInsetsMake(-3, -14, -5, -16));//扩宽可点区域
NSLog(@"button frame = %@",NSStringFromCGRect(_customBtn.frame));
}
return self;
}
-(void)isinAction{
NSLog(@"Hello Kit");
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(150, 0, 10, 10)];
label.backgroundColor = [UIColor greenColor];
[self.window addSubview:label];
[UIView animateWithDuration:0.5 animations:^{
label.frame = CGRectMake(150, 200, 10, 10);
} completion:^(BOOL finished) {
[label removeFromSuperview];
}];
}
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
CGPoint newPoint = [self convertPoint:point toView:self.customBtn];
BOOL isINside = [self.customBtn pointInside:newPoint withEvent:event];
if (isINside) {
return self.customBtn;
}else{
[super hitTest:point withEvent:event];
}
NSLog(@"进入C_View---hitTest withEvent ---");
UIView * view = [super hitTest:point withEvent:event];
NSLog(@"离开C_View---hitTest withEvent ---hitTestView:%@",view);
return view;
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
NSLog(@"C_view---pointInside withEvent ---");
BOOL isInside = [super pointInside:point withEvent:event];
NSLog(@"C_view---pointInside withEvent --- isInside:%d",isInside);
return isInside;
}