欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-08 23:19:33
...

一 、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的区别以及事件传递机制和视图响应链
hitTest:withEvent:方法的系统实现图:
iOS UIView和CALayer的区别以及事件传递机制和视图响应链

由上图的流程图中可以得知:
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的区别以及事件传递机制和视图响应链

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

易错点:

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

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

答案:

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

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