Event Delivery & Responder
2019-08-14 本文已影响0人
ienos
三大事件
触摸事件、传感事件、远程控制事件
六种手势识别器
点击、拖拽、滑动、缩放、旋转、长按
UITouch & UIEvent
- 一个手指会关联一个 UITouch
- 每产生一个事件就会产生一个 UIEvent(类型、时间)
响应者对象 UIResponder
响应链上的响应者会同时处理触摸事件,能够在多个 view 内打印出 touchBegan:withEvent:
// 触摸事件
- (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;
// 传感器事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
// 远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
与加速计、陀螺仪和磁力计相关的运动事件不遵循响应者链,Core Motion 会将这些事件直接传递给我们指定的对象
事件的产生和传递
- 产生事件之后,会将事件传递给 UIApplication 管理的队列事件中
- UIApplication 会从队列事件中找出最前的事件,然后分发下去,通常会将事件传递给主窗口(keyWindow)
- 主窗口在视图层次结构中找到一个最合适的视图来处理事件
- 从父视图传递到子视图,如果确认父视图能响应事件,则遍历子视图是否存在能够响应的视图,如果没有,则为父视图响应;若有子视图响应,则继续该视图的子视图
如何寻找一个合适的 View ?
- 视图开始调用
hitTest:withEvent:和pointInside:withEvent:(先hitTest:withEvent:再pointInside:withEvent:),hitTest:withEvent:会循环调用子视图的hitTest:withEvent:,hitTest:withEvent:返回的就是最终响应的视图,若该分支的视图无法响应,最终返回 null - 从 UIWindow 开始,对视图的每一个子视图依次调用,子视图的调用顺序是从后面往前面
alpha=0、子视图超出父视图、userInteractionEnabled=NO、hidden=YES 这四种情况下
hitTest:withEvent:不会调用
hitTest:withEvent: & pointInside:withEvent:
// 返回视图层级中能响应触控点的最深视图
// point是该视图的坐标系上的点
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 1.判断自己能否接收触摸事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
// 2.判断触摸点在不在自己范围内
if (![self pointInside:point withEvent:event]) return nil;
// 3.从后往前遍历自己的子控件,看是否有子控件更适合响应此事件
int count = self.subviews.count;
for (int i = count - 1; i >= 0; i--) {
UIView *childView = self.subviews[i];
CGPoint childPoint = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childPoint withEvent:event];
if (fitView) {
return fitView;
}
}
// 没有找到比自己更合适的view
return self;
}
// 返回视图是否包含指定的某个点,返回 YES 向所有当前视图的子视图发送 hitTest:withEvent
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
事件的响应
响应者(Responder): 继承 UIResponder 的对象称为响应者对象
响应者链条(Responder Chain): 很多响应者链接在一起组合起来的一个链条称为响应者链(View -> SuperView -> ViewController -> UIWindow -> UIApplication -> AppDelegate)
处理者
找到 Responder 之后,如果 Responder 没处理,事件会被传递,会在 Responder Chain 下一个事件处理者,如果每一个 Responder 都不处理,则事件会被丢弃