iOS中的消息传递

52 阅读2分钟

  1. 事件传递

  2. iOS中事件有:触摸、远程事件、加速器

  3. 屏幕接收到触摸,系统把事件传递到对应的app,app的runloop通过注册好的监听接口接收到event,开始从最底层的父视图往下找最终能相应的视图。

  4. 寻找顺序uiapplication->uiwindow->rootviewcontroller->uiview->subview->最终相应的视图

  5. 寻找实现的方法- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event   

  6. 在hittest方法中,是否传递的三个条件:a、userinteractionenabled是否可交互。b、alpha 不等于0。c、没有hidden。d、点击坐标在当前视图上。如果这几个条件都满足,就开始遍历自己的子视图,让子视图调用hittest方法。遍历子视图的顺序是优先遍历最后添加的子视图。

  7. Hit-Test 优先检测自己是否命中,不命中则直接忽略所有 subviews

  8. Hit-Test 若自己命中,则对所有子视图按同层级视图顺序从前向后的顺序依次进行碰撞检测,因此碰撞检测也是 superview 到 subview 的按视图层级从后向前递归的过程;

  9. Hit-Test 若未命中任何子视图,自己的碰撞检测才返回 nil;

    • (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] == NO) return nil; // 3.从后往前遍历子控件数组 int count = (int)self.subviews.count; for (int i = count - 1; i >= 0; i--) { // 获取子控件 UIView *childView = self.subviews[i]; // 坐标系的转换,把窗口上的点转换为子控件上的点 // 把自己控件上的点转换成子控件上的点 CGPoint childP = [self convertPoint:point toView:childView]; UIView *fitView = [childView hitTest:childP withEvent:event]; if (fitView) { // 如果能找到最合适的view return fitView; } } // 4.没有找到更合适的view,也就是没有比自己更合适的view return self;

事件响应

  1. uiresponder、uigesturerecgonize、uicontrol 优先级逐渐增强
  2. 消息响应链,从子视图逐层往父视图找,一直到uiwindow、uiaplication
  3. 如果uicontrol 中的target设置为nill,依然按照响应链往上找,知道找到能响应方法的视图。
  4. 响应链是以view为其实,向superview延伸的一个反向树形结构,通过UIRestponder的nestResponder串联而成。
  5. 响应链 view->controller->uiwindow->windowscene->application->appdelegate
  6. UIResponder作为第一响应者响应了 touch 事件,响应链后面的所有响应者也会触发touchesXXX系列方法;
  7. UIControl控件作为第一响应者响应了 touch 事件,响应链后面的所有响应者均不再处理该 touch 事件;