这是OC基础的最终一个章节啦,这节主要给咱们讲讲呼应者链条

1. 呼应者链条

关于呼应者链条,信任咱们或许听说过这么一句话:事情由上往下传递,呼应由下往上传递,那么这句话是什么意思呢?

咱们知道,在写UI时,每个UI控件,或是UI视图,都是从最初的一个UIView上,不断调用addSubview办法,叠加在父view上,进行展示的。

例如,假定咱们有下面这么一段代码:

UIView *view1, *view2;
UIButton *btn1;view1 = [UIView new];
view2 = [UIView new];
btn1 = [UIButton new];[view1 addSubview:view2];
[view2 addSubview:btn1];

显而易见,view2的父视图为view1,btn1的父视图为view2。

如果咱们现在在btn1上有一个点击事情,那么这个点击事情会直接传递给btn1吗? 答案是否定的,由于事情是由上往下传递的,这个事情会先传递给view1,再传递给view2,最终传递给btn1。

事情传递主要依托下面这个函数来完成:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  // 3种状况无法呼应事情,1.用户交互被禁用;2.当时视图被躲藏;3.当时视图透明度小于0.01(跟被躲藏了差不多)
  if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
 
  // 触摸点若不在当时视图上则无法呼应事情
  if ([self pointInside:point withEvent:event] == NO) return nil;
 
  // 从后往前遍历子视图数组
  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){
      // 如果子视图中有更合适的就回来
      return fitView;
     }
   }
 
  // 没有在子视图中找到更合适的呼应视图,那么本身便是最合适的
  return self;
}

从函数中能够看出,事情传递的流程为:

  1. 如果当时视图无法呼应事情,则回来nil
  2. 如果当时点击处在当时视图可呼应规模之外,则回来nil
  3. 从后往前遍历子视图,如果子视图能够处理当时事情,则回来子视图
  4. 不然回来本身视图

其间,第3步中,从后往前而不是从前往后遍历子视图的原因是:后参加的子视图会掩盖在先前参加的子视图之上,从用户角度来说,用户期望得到呼应的视图应该是能够被看见的视图,然后参加的子视图由于会掩盖在最顶层所以更容易被用户看见,因而应该从后往前遍历。

此外,咱们也应该注意pointInside: withEvent:这个办法,是很多面试官爱考的考点。 咱们能够经过overwrite这个办法,来改动一个视图能够呼应的规模(默许能够呼应的规模是这个视图包括的屏幕区域)。

关于呼应从下往上传递:咱们在将事情从上往下传递后,使用- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event办法找到的最合适的呼应视图,并不一定能够处理当时事情,仍按照上面的例子来说,假定咱们有如下代码:

UIView *view1, *view2;
UIButton *btn1;view1 = [UIView new];
view2 = [UIView new];
view2.userInteractionEnabled = YES;
btn1 = [UIButton new];[view1 addSubview:btn1];
[btn1 addSubview:view2];

注意与之前的区别,现在view2的父视图为btn1,而btn1的父视图为view1,view2和btn1的父子关系相互调换了。 此外,咱们还敞开了view2的用户交互属性。

如果咱们现在在btn1上有一个点击事情,按照事情从上往下传递的流程,咱们会找到view2。 但是,咱们会发现view2并不能够处理这个点击事情,因而,这个点击事情便由下往上传递给了btn1,并交由btn1处理(btn1能够处理,则调用相应的呼应办法进行处理)。 假定btn1仍然不能处理,则持续往上传递给view1,直至事情被处理或许最终被丢弃。

2. UIButton的承继关系

UIButton的承继关系为:

UIButton --> UIControl --> UIView --> UIResponder --> NSObject

咱们要注意UIResponder和UIControl的区别:

  1. UIResponder能够呼应某个事情,使用touchesBegan: withEvent:办法(自己的事情自己做
  2. UIControl不只本身能够呼应某个事情,还能够使用addTarget: forSelector: withEvent:为指定的某个方针增加事情(交给别人来做

例如,普通的UIView想要呼应事情,只能依托本身完成touchesBegan: withEvent:办法; 而UIButton想要呼应事情,不只能够依托本身,还能够将这个事情绑定到一个方针方针上,依托方针方针的某个办法来处理事情

好啦,OC基础到这儿就讲完啦,下期开始讲Runtime,欢迎持续重视! :)

个人大众号:iOS开发学习

未经作者答应,禁止转载!