文章目录
- 简述
- Hit-Test 机制 (找到最佳响应者)
- 响应者链 Responder chain
- 应用
- 获取当前View的控制器对象:
- 手势穿透:
简述
-
传递链:
系统向离用户最近的view传递。
UIKit –> active app’s event queue –> window –> root view –> … –> lowest view -
响应链:
由离用户最近的view向系统传递。
initial view –> super view –> … –> view controller –> window –> Application –> AppDelegate
iOS中,只有继成UIResponder的对象才能接收并处理事件,UIReaponder是所有响应对象的基类,在UIResponder类中定义了处理上述各种事件的接口。UIApplication、UIViewController、UIWindow和所有继承直UIView和UIKit类都直接或间接的继承UIResponder,所以它们的实例都是可以构成响应者链的响应者对象。
Hit-Test 机制 (找到最佳响应者)
用户触摸,Touch -> UIEvent -> eventQueue -> UIApp -> UIWindow -> hitTest:withEvent: 查找触摸点所在视图。
当用户触摸屏幕时,系统首先要找到响应者Responder。将Touch以UIEvent的方式加入到UIApplication的事件队列中。UIApplication从事件队列中取出最新的事件传递到UIWindow。UIWindow通过 hitTest:withEvent: 方法寻找触摸点所在的视图,这个过程称之为hit-test view。
UIApplication -> UIWindow -> RootView -> supView -> View
在顶级视图(Root View)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;
如果返回NO,那么hitTest:withEvent:返回nil;
如果返回YES,那么它会向当前视图的所有子视图发送hitTest:withEvent:消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。
响应者链 Responder chain
当系统通过Hit-Test机制找到触摸到的View,但是view没有或无法正确处理此次Touch。此时,系统便会通过响应者链寻找下一个响应者,对此次Touch进行相应:
View -> View Controller(如果存在)-> superView -> RootView -> UIWindow -> UIApplication
hitTest 实现原理大致如下:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {// 1、控件不允许与用户交互if (self.userInteractionEnabled == NO ||self.alpha <= 0.01 ||self.hidden == YES) {return nil;}// 2、点击的point不在当前控件内if (![self pointInside:point withEvent:event]) {return nil;}// 3、倒序遍历每一个子控件for (int i = (int)self.subviews.count - 1; i >= 0; i--) {UIView *childView = self.subviews[i];// 当前触控Point的坐标转换为相对于子控件的坐标CGPoint childPoint = [self convertPoint:point toView:childView];// 在子控件中找能响应的子控件(递归循环),从上层找起UIView *fitView = [childView hitTest:childPoint withEvent:event];if (fitView) {return fitView;}}// 4、子视图中没有能响应的view,就返回自己return self;
}// 该方法判断触摸点是否在控件上,point必须是方法调用者的坐标
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {return NO;
}
应用
获取当前View的控制器对象:
- (UIViewController *)getCurrentViewController{ UIResponder *next = [self nextResponder]; do { if ([next isKindOfClass:[UIViewController class]]) { return (UIViewController *)next; } next = [next nextResponder]; } while (next != nil); return nil;
}
手势穿透:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {UIView *view = [super hitTest:point withEvent:event];if (self == view) {return nil;}return v;
}