美文网首页
iOS全解14:事件的传递和响应机制

iOS全解14:事件的传递和响应机制

作者: lukyy | 来源:发表于2021-11-14 12:50 被阅读0次

    按照时间顺序,事件的生命周期是这样的:
      事件的产生和传递(事件如何从父控件传递到子控件并寻找到最合适的view、寻找最合适的view的底层实现、拦截事件的处理)->找到最合适的view后事件的处理(touches方法的重写,也就是事件的响应)

    在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。以下都是继承自UIResponder的,所以都能接收并处理事件。

    UIApplication
    UIViewController
    UIView(superView、subView)

    事件的传递和响应的区别:

    事件的传递:是从上到下(父控件到子控件)
    事件的响应:是从下到上(顺着响应者链条向上传递:子控件到父控件)

    UITouch对象

    当用户用手指触摸屏幕时,会创建一个与手指相关的UITouch对象。

    作用:
    • 保存着跟手指相关的信息,比如触摸的位置、时间、阶段
    • 当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
    • 当手指离开屏幕时,系统会销毁相应的UITouch对象
      提 示:iPhone开发中,要避免使用双击事件!

    iOS中的事件的产生和传递

    1、事件的产生
    • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,为什么是队列而不是栈?因为队列的特点是FIFO,即先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列。
    • UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
    • 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。
      找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。
    2、事件的传递

    触摸事件的传递是从父控件传递到子控件
    也就是UIApplication->window->寻找处理事件最合适的view
    注 意:如果父控件不能接受触摸事件,那么子控件就不可能接收到触摸事件

    应用如何找到最合适的控件来处理事件?

    1.首先判断主窗口(keyWindow)自己是否能接受触摸事件
    2.判断触摸点是否在自己身上
    3.子控件数组中从后往前遍历子控件,重复前面的两个步骤(所谓从后往前遍历子控件,就是首先查找子控件数组中最后一个元素,然后执行1、2步骤)
    4.view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
    5.如果没有符合条件的子控件,那么就认为自己最合适处理这个事件,也就是自己是最合适的view。
    UIView不能接收触摸事件的三种情况:

    • 不允许交互:userInteractionEnabled = NO
    • 隐藏:如果把父控件隐藏,那么子控件也会隐藏,隐藏的控件不能接受事件
    • 透明度:如果设置一个控件的透明度<0.01,会直接影响子控件的透明度。alpha:0.0~0.01为透明。
      注 意:默认UIImageView不能接受触摸事件,因为不允许交互,即userInteractionEnabled = NO。所以如果希望UIImageView可以交互,需要设置UIImageView的userInteractionEnabled = YES。

    总结一下

    1.点击一个UIView或产生一个触摸事件A,这个触摸事件A会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
    2.UIApplication会从事件对列中取出最前面的事件(此处假设为触摸事件A),把事件A传递给应用程序的主窗口(keyWindow)。
    3.窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)



    //==================" 系统框架 "==================

    #import <Foundation/Foundation.h>
    #import <CoreGraphics/CoreGraphics.h>
    #import <UIKit/UIKitDefines.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @class UIWindow, UIView, UIGestureRecognizer;
    
    typedef NS_ENUM(NSInteger, UITouchPhase) {
        UITouchPhaseBegan,             // 当手指接触表面时。
        UITouchPhaseMoved,             // 当手指在表面移动。
        UITouchPhaseStationary,        // 当手指接触表面,但自上次事件后没有移动。       
        UITouchPhaseEnded,             // 当手指离开表面时。
        UITouchPhaseCancelled,         // 当触摸没有结束,但我们需要停止跟踪时(例如,将设备面对面)
        UITouchPhaseRegionEntered   API_AVAILABLE(ios(13.4) // 当触摸进入用户界面区域时
        UITouchPhaseRegionMoved     API_AVAILABLE(ios(13.4) // 当触摸在用户界面的区域内,但还没有接触或离开该区域时
        UITouchPhaseRegionExited    API_AVAILABLE(ios(13.4) // 当触摸退出用户界面区域时
    };
    
    typedef NS_ENUM(NSInteger, UIForceTouchCapability) {
        UIForceTouchCapabilityUnknown = 0,
        UIForceTouchCapabilityUnavailable = 1,
        UIForceTouchCapabilityAvailable = 2
    };
    
    typedef NS_ENUM(NSInteger, UITouchType) {
        UITouchTypeDirect,                       // 用手指(在屏幕上)直接触摸
        UITouchTypeIndirect,                     // 间接触摸(不是屏幕)
        UITouchTypePencil,                      // 添加铅笔名变体
        UITouchTypeStylus = UITouchTypePencil,  // 触控笔的触摸(不建议使用铅笔)
        UITouchTypeIndirectPointer API_AVAILABLE(ios(13.4) //一种表示基于按钮的间接输入设备的触摸,描述从按钮按下到按钮释放的输入顺序
    } API_AVAILABLE(ios(9.0));
    
    typedef NS_OPTIONS(NSInteger, UITouchProperties) {
        UITouchPropertyForce = (1UL << 0),
        UITouchPropertyAzimuth = (1UL << 1),
        UITouchPropertyAltitude = (1UL << 2),
        UITouchPropertyLocation = (1UL << 3), // 为预测触动
    } API_AVAILABLE(ios(9.1));
    
    UIKIT_EXTERN API_AVAILABLE(ios(2.0)) NS_SWIFT_UI_ACTOR
    @interface UITouch : NSObject
    
    @property(nonatomic,readonly) NSTimeInterval    timestamp;      // 记录了触摸事件产生或变化时的时间,单位是秒
    @property(nonatomic,readonly) UITouchPhase  phase;          // 当前触摸事件所处的状态
    @property(nonatomic,readonly) NSUInteger        tapCount;       // 短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
    @property(nonatomic,readonly) UITouchType       type 
    @property(nonatomic,readonly) CGFloat majorRadius;          // majorRadius和majorRadiusTolerance以点数表示
    @property(nonatomic,readonly) CGFloat majorRadiusTolerance; // majorRadius将是精确的+/- majorRadiusTolerance
    @property(nullable,nonatomic,readonly,strong) UIWindow                        *window;          // 触摸产生时所处的窗口
    @property(nullable,nonatomic,readonly,strong) UIView                          *view;                // 触摸产生时所处的视图
    @property(nullable,nonatomic,readonly,copy)   NSArray <UIGestureRecognizer *> *gestureRecognizers; // 触摸手势
    
    - (CGPoint)locationInView:(UIView *)view;                   // 触摸在view上的位置
    - (CGPoint)previousLocationInView:(UIView *)view;       // 记录了前一个触摸点的位置
    - (CGPoint)preciseLocationInView:(UIView *)view;            // 使用这些方法获得额外的精度,可能从触摸可用。
    - (CGPoint)precisePreviousLocationInView:(UIView *)view; // 不要使用精确的位置进行命中测试。触摸可能会在视图内部进行测试,但在视图外部有一个精确的位置。
    
    @property(nonatomic,readonly) CGFloat force;                    //触摸的力,其中1.0代表平均触摸的力
    @property(nonatomic,readonly) CGFloat maximumPossibleForce; //使用这个输入机制最大可能的力量
    
    //1 方位角。仅对触控笔触摸类型有效。零弧度点沿X轴正方向。为视图参数传递一个空值将返回相对于触摸窗口的方位角。
    //2 指向方位角方向的单位向量。仅对触控笔触摸类型有效。为视图参数传递nil将返回一个相对于触摸窗口的单位向量。
    - (CGFloat)azimuthAngleInView:(nullable UIView *)view;
    - (CGVector)azimuthUnitVectorInView:(nullable UIView *)view;
    
    // 高度角:仅对触控笔触摸类型有效。0弧度表示触控笔与屏幕表面平行,当M_PI/2弧度表示它是垂直于屏幕表面。
    @property(nonatomic,readonly) CGFloat altitudeAngle;
    
    //1 一个索引,允许你关联更新与原始触摸。只有当这个UITouch预期或是一个更新时才保证非nil。
    //2 一个属性的集合,它具有估计值,仅表示当前估计的属性
    //3 期望在将来有更新的属性集。如果未对估价值进行更新,则当前值为最终估价值。当从边缘进入时,方位/高度值会发生这种情况
    @property(nonatomic,readonly) NSNumber * _Nullable estimationUpdateIndex;
    @property(nonatomic,readonly) UITouchProperties estimatedProperties;
    @property(nonatomic,readonly) UITouchProperties estimatedPropertiesExpectingUpdates;
    
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    #else
    #import <UIKitCore/UITouch.h>
    #endif
    

    //==================" 类别 "==================

    @interface UIView(UIViewGeometry)
    
    //可以做成动画。如果视图被转换,不要使用框架,因为它不能正确地反映视图的实际位置。使用bounds + center代替。
    @property(nonatomic) CGRect            frame;
    
    //如果非恒等变换,则使用bounds/center而不是frame。如果边界维数为奇数,中心可能有小数部分
    @property(nonatomic) CGRect            bounds;      // 默认边界是0原点,帧大小。可以做成动画
    @property(nonatomic) CGPoint           center;      // 中心是框架的中心。可以做成动画
    @property(nonatomic) CGAffineTransform transform;   // 默认是CGAffineTransformIdentity。可以做成动画。请使用这个属性而不是图层上的affineTransform属性
    @property(nonatomic) CATransform3D     transform3D API_AVAILABLE(ios(13.0),tvos(13.0)); //默认为CATransform3DIdentity。可以做成动画。请使用这个属性而不是图层上的transform属性
    @property(nonatomic) CGFloat           contentScaleFactor API_AVAILABLE(ios(4.0));
    
    @property(nonatomic,getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled API_UNAVAILABLE(tvos);   // default is NO
    @property(nonatomic,getter=isExclusiveTouch) BOOL       exclusiveTouch API_UNAVAILABLE(tvos);         // default is NO
    
    - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // 递归调用-pointInside:withEvent:。点在接收器的坐标系统中
    - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;        // 如果点在边界内,默认返回YES(判断点在不在方法调用者的坐标系上)
    - (CGPoint)convertPoint:(CGPoint)point toView:(nullable UIView *)view;
    - (CGPoint)convertPoint:(CGPoint)point fromView:(nullable UIView *)view;
    - (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
    - (CGRect)convertRect:(CGRect)rect fromView:(nullable UIView *)view;
    
    @property(nonatomic) BOOL               autoresizesSubviews; // 默认为YES。如果设置,子视图将根据它们的autoresizingMask if self进行调整。范围内变化
    @property(nonatomic) UIViewAutoresizing autoresizingMask;    // 简单的调整。默认是UIViewAutoresizingNone
    
    - (CGSize)sizeThatFits:(CGSize)size;    // 返回'best' size以适合给定的大小。实际上不会调整视图的大小。默认是返回现有的视图大小
    - (void)sizeToFit;                  // 调用sizeethatfits:与当前视图边界和改变边界大小。
    
    @end
    

    引用:
    史上最详细的iOS之事件的传递和响应机制-原理篇
    UIView超出父view的部分视图的子视图响应事件

    相关文章

      网友评论

          本文标题:iOS全解14:事件的传递和响应机制

          本文链接:https://www.haomeiwen.com/subject/kdattrtx.html