文章目录
- iOS事件
- 传递过程
- 响应者链过程示意图
- 代码实现
- UIResponder 响应者对象
- UIResponder内部提供的响应和处理事件的方法
- UITouch
- 介绍
- 主要属性和方法
- UIEvent
- 介绍
- 主要属性和方法
- UIView不能接收事件的三种情况
- 参考文献
iOS事件
- 触摸事件(touch events)
- 按压事件(press events)
- 摇晃事件(shake - motions events)
- 远程控制事件 (remote - controls events)
- 编辑菜单消息事件(editing menu messages)
这篇博客主要以触摸事件为例
传递过程
- 主窗口接收到应用传过来的事件后,首先判断能不能接收事件。如果能,就会判断触摸点在不在自身范围内。(通过pointInside:withEvent判断)如果不在,就不处理。如果在就进行第二步。
- 如果触摸点在自己的坐标范围内,那么窗口会从后往前遍历自己的子控件,来寻找最合适的view。(通过hitTest:withEvent:方法递归寻找)
- 遍历到每一个子控件后,又会重复上面的两个步骤(传递事件给子控件,1.判断子控件能否接受事件,2.点在不在子控件上)
- 按照上面步骤循环遍历子控件,直到找到最合适的view,如果没有更合适的子控件,那么自己就是最合适的view。
响应者链过程示意图
代码实现
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.alpha <= 0.01 || self.userInteractionEnabled == NO || self.hidden) {
return nil;
}
BOOL inside = [self pointInside:point withEvent:event];
if (inside) {
NSArray *subViews = self.subviews;
// 对子视图从上向下找
for (NSInteger i = subViews.count - 1; i >= 0; i--) {
UIView *subView = subViews[i];
CGPoint insidePoint = [self convertPoint:point toView:subView];
UIView *hitView = [subView hitTest:insidePoint withEvent:event];
if (hitView) {
return hitView;
}
}
return self;
}
return nil;
}
方法讲解
//搜索是在哪个视图
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
//判断是否在这个视图范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
UIResponder 响应者对象
UIResponder是iOS中用于处理用户事件的API,可以处理触摸事件、按压事件(3D touch)、远程控制事件、硬件运动事件。可以通过touchesBegan、pressesBegan、motionBegan、remoteControlReceivedWithEvent等方法,获取到对应的回调消息。UIResponder不只用来接收事件,还可以处理和传递对应的事件,如果当前响应者不能处理,则转发给其他合适的响应者处理。
应用程序通过响应者来接收和处理事件,响应者可以是继承自UIResponder的任何子类,例如UIView、UIViewController、UIApplication等。当事件来到时,系统会将事件传递给合适的响应者,并且将其成为第一响应者。
第一响应者未处理的事件,将会在响应者链中进行传递,传递规则由UIResponder的nextResponder决定,可以通过重写该属性来决定传递规则。当一个事件到来时,第一响应者没有接收消息,则顺着响应者链向后传递。
UIResponder内部提供的响应和处理事件的方法
UIView是UIResponder的子类,可以覆盖下列4个方法处理不同的触摸事件
一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指离开view,系统会自动调用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
- touches 里包含了一个或多UITouch对象,也即一个或多个手指同时触摸view,因此touches.count就是触摸的点数,是1就是单点触摸,大于1就是多点触摸。
- 一次触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数
如果两根手指同时触摸一个view,那就是一个事件,view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象 - 如果这两根手指一前一后分开触摸同一个view,那就是两个事件,view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象
上面方法有两个参数,UITouch和UIEvent,解释如下:
UITouch
介绍
当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象,一根手指对应一个UITouch对象,它保存着跟手指相关的信息,比如触摸的位置,时间,阶段等,当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置,当手指离开屏幕时,系统会销毁相应的UITouch对象
主要属性和方法
//触摸产生时所处的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
//触摸产生时所处的视图
@property(nonatomic,readonly,retain) UIView *view;
//短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger tapCount;
//记录了触摸事件产生或变化时的时间,单位是秒
@property(nonatomic,readonly) NSTimeInterval timestamp;
//当前触摸事件所处的状态
@property(nonatomic,readonly) UITouchPhase phase;
//获取手指与屏幕的接触半径 IOS8以后可用 只读
@property(nonatomic,readonly) CGFloat majorRadius NS_AVAILABLE_IOS(8_0);
//获取手指与屏幕的接触半径的误差 IOS8以后可用 只读
@property(nonatomic,readonly) CGFloat majorRadiusTolerance NS_AVAILABLE_IOS(8_0);
//获取触摸手势
@property(nullable,nonatomic,readonly,copy) NSArray <UIGestureRecognizer *> *gestureRecognizers NS_AVAILABLE_IOS(3_2);
//获取触摸压力值,一般的压力感应值为1.0 IOS9 只读
@property(nonatomic,readonly) CGFloat force NS_AVAILABLE_IOS(9_0);
//获取最大触摸压力值
@property(nonatomic,readonly) CGFloat maximumPossibleForce NS_AVAILABLE_IOS(9_0);
//取得在指定视图的位置
// 返回值表示触摸在view上的位置
// 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0,0))
// 调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
- (CGPoint)locationInView:(nullable UIView *)view;
//该方法记录了前一个触摸点的位置
- (CGPoint)previousLocationInView:(nullable UIView *)view;
UIEvent
介绍
称为事件对象,记录事件产生的时刻和类型。每产生一个事件,都会产生一个UIEvent对象。
主要属性和方法
//事件类型
@property(nonatomic,readonly) UIEventType type;
//事件子类型
@property(nonatomic,readonly) UIEventSubtype subtype;
//事件产生的时间
@property(nonatomic,readonly) NSTimeInterval timestamp;
//返回值:返回与接收器相关联的所有触摸对象。
- (nullable NSSet <UITouch *> *)allTouches;
// 返回值:返回属于一个给定视图的触摸对象,用于表示由接收器所表示的事件。
- (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view;
//返回值:返回属于一个给定窗口的接收器的事件响应的触摸对象。
- (nullable NSSet <UITouch *> *)touchesForWindow:(UIWindow *)window;
//返回值:返回触摸对象被传递到特殊手势识别
- (nullable NSSet <UITouch *> *)touchesForGestureRecognizer:(UIGestureRecognizer *)gesture
UIView不能接收事件的三种情况
- 不允许交互
userInteractionEnabled
= NO - 透明度
alpha
< 0.01 - 父视图或者子视图的
hidden
= YES
参考文献