iOS UIView和CALayer的区别以及事件传递机制和视图响应链

一 、UIView和CALayer的区别

  1. UIView继承自继承自UIResponder的类,只有继承UIResponder的类才能处理事件(如触摸、点击等事件),参与事件响应链,而CALayer继承自NSObject,不能处理事件。
  2. UIView侧重于内容显示的管理,CALayer侧重于内容的绘制。
  3. UIView和CALyer是相互依赖的关系。UIView的显示依赖于CALyer的提供的内容,而CALyer则依赖UIView的容器来显示绘制的内容。
  4. UIView来自CALyer,高于CALyer,是CALyer的高层实现和封装,UIView中的所有特性来源于CALyer的支持。

二、只有继承UIResponder的的类,才能处理事件。

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer, UIFocusEnvironment>
    NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, UIFocusItemContainer, CALayerDelegate>
    NS_CLASS_AVAILABLE_IOS(2_0) @interface UIApplication : UIResponder
    @interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>

可以看出UIVIew、UIViewController、UIApplication都继承自UIResponder类,可以响应和处理事件,而CALyer不是UIResponder类,不能处理事件。

继承自UIResponder类有这些方法:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

重写这些方法一定要先调用他的父类的方法也就是:[super touchesBegan:touches withEvent:event];不然程序就不会触发touches方法,那么事件就不会继续向上传递了。

继承自UIView的类有两个主要方法:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
    -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

只有继承UIView的视图类才能重写这两个方法。
1、pointInside:withEvent:方法:是用来判断触摸点是否在当前视图范围内,如果再当前视图范围内,就返回YES,否则返回NO。
2、hitTest:withEvent方法:哪个视图响应这个事件,返回的就是这个视图。(返回的View是本次点击事件需要的最佳View)。

二、UIView的事件传递流程

UIview、UIApplication、UIViewController这些类是继承于UIresponder的类,这些类是可以响应和处理事件的。

事件传递流程图:
iOS UIView和CALayer的区别以及事件传递机制和视图响应链_第1张图片
hitTest:withEvent:方法的系统实现图:
iOS UIView和CALayer的区别以及事件传递机制和视图响应链_第2张图片

由上图的流程图中可以得知:
1、当iOS程序中发生了触摸事件后,系统会将事件加入到UIApplication管理的一个队列任务中。
2、UIApplication会将这个任务向下分发传递给UIWindow。
3、UIWindow会通过hitTest:withEvent:方法返回事件响应的视图,也就是触摸点所在的视图。
4、hitTest:withEvent:方法中又会调用pointInside:withEvent:方法。来判断触摸点是否在当前视图范围内。
5、在hitTest:withEvent:方法中判断如果当前视图不与用户交互,或者隐藏,或者透明度小于等于0.01的话,返回nil,执行的就是下面的这就代码:if (!self.userInteractionEnabled || self.isHidden || self.alpha <= 0.01) { return nil; }
6、如果触摸点在当前视图范围内,会倒序遍历当前视图的子视图。调用子视图的hitTest:withEvent:方法,如果当前视图是非空的子视图对象的话,则停止遍历,返回这个非空的视图对象。如果当前视图为空的话,则返回当前视图。

三、以上这些讲述了事件传递流程,那么传递完了事件之后,这个事件最终由谁来响应呢?这就涉及到了视图的响应链:包含了视图响应链的机制和流程。

视图响应流程:
iOS UIView和CALayer的区别以及事件传递机制和视图响应链_第3张图片

上图为苹果官网给的一个视图响应流程:
可以看到iOS设备中的三个控件的响应者就是当前包含这三个控件的一个view,这时有可能view上层还有一个viewController的view,再接着这个view的响应者就是UIViewController,UIViewController的下一个响应者是UIWindow,UIWindow的下一个响应者就是程序的入口:UIApplication,UIApplication还有一个代理响应者:UIApplicationDelegate。

易错点:

首先看图:
iOS UIView和CALayer的区别以及事件传递机制和视图响应链_第4张图片

问题: 如果点击的View C2的空白圆的位置,最终这个事件是否由C2来处理的话,就涉及到视图事件响应链的流程和机制。现在点击View C2的空白圆的位置,会先由C2来接收这个响应事件,如果C2没有处理这个事件,那么它就会把事件传递给他的下一个响应者B2,如果B2也不处理这个事件的话,那他就会把这个事件传递给他的下一个响应者,也就是他的直接父视图View A,最终如果A还是不响应事件的话,那么他就会沿着响应链向上传递,直到传递到UIApplicationDelegate,如果最终仍然没有视图去响应这个事件的话,那么这个事件最终会怎么样?

答案:

其实答案很简单,如果最终仍然没有视图去响应这个事件,那么程序就会忽略这个事件,当做什么事情都没有发生。(并不会引起程序的崩溃)

事件传递机制的流程demo地址:
  • 事件传递机制的流程

你可能感兴趣的:(iOS UIView和CALayer的区别以及事件传递机制和视图响应链)