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

2021-11-14  本文已影响0人  lukyy

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

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

UIApplication
UIViewController
UIView(superView、subView)

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

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

UITouch对象

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

作用:

iOS中的事件的产生和传递

1、事件的产生
2、事件的传递

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

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

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

总结一下

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的部分视图的子视图响应事件

上一篇下一篇

猜你喜欢

热点阅读