ios和android事件传递,IOS事件的传递和响应机制
1 IOS事件IOS中的事件有3大类型:· 触摸类型· 加速加速计事件· 远程控制事件本文只讨论IOS的触摸事件1.1响应者对象(UIResponder)在IOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。UIApplication、UIViewController、UIView都继承自UIResponder,均能接受并处理事件。
1 IOS事件
IOS中的事件有3大类型:
· 触摸类型
· 加速加速计事件
· 远程控制事件
本文只讨论IOS的触摸事件
1.1响应者对象(UIResponder)
在IOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,我们称之为“响应者对象”。
UIApplication、UIViewController、UIView都继承自UIResponder,均能接受并处理事件。
那UIResponder为什么能接受并处理事件呢?因为UIResponder提供了4个对象方法来处理触摸事件。
UIResponder内部提供了以下方法来处理事件触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
2 事件的处理
下面以UIView为例来说明触摸事件的处理。
// 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,那么view只会调用一次touchBegin:withEvent:方法,touches参数中装着两个UITouch对象
· 如果两根手指一前一后分开触摸同一个View,那么view会调用两次touchBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象
· 重写以上四个方法,如果是处理UIView的触摸事件,必须要自定义UIView子类继承自UIView。因为苹果不开源,我们无法修改UIView.m文件,只能通过子类继承父类,重写父类方法来处理UIView的触摸事件()
· 上面我们说的是UIVIew的触摸事件,但是如果要处理UIViewController的触摸事件,那么直接在控制器的.m文件中重写四个方法即可
2.1 UIView的拖拽
我们见过很多场景,可以将一个对象拖来拖去,那怎样实现的呢?即让UIView随着手指的移动而 移动。
那就是重写touchMoved:withEvent:方法
此时需要用到参数touches。
2.1.1 UITouch对象
· 当用户用一根手指触摸屏幕的时候,会创建一个与手指相关的UITouch的对象。
· 一根手指对应一个UITouch对象
· 如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着两个UITouch对象
· 如果这两根手指一前一后分开触摸同一个view,那么view会分别调用两次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象
2.1.1.1 UITouch的作用
· 保持着根手指相关的信息,比如触摸的位置、事件和阶段
· 当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指所在的触摸位置
· 当手指离开屏幕时,系统会销毁相应的UITouch对象
iPhone开发中,避免双击事件
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;
2.1.1.2 UITouch方法
(CGPoint)locationInView:(UIView *)view;
// 返回值表示触摸在view上的位置
// 这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
// 调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
(CGPoint)previousLocationInView:(UIView *)view;
// 该方法记录了前一个触摸点的位置
代码实现
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
// 想让控件随着手指移动而移动,监听手指移动
// 获取UITouch对象
UITouch *touch = [touches anyObject];
// 获取当前点的位置
CGPoint curP = [touch locationInView:self];
// 获取上一个点的位置
CGPoint preP = [touch previousLocationInView:self];
// 获取它们x轴的偏移量,每次都是相对上一次
CGFloat offsetX = curP.x - preP.x;
// 获取y轴的偏移量
CGFloat offsetY = curP.y - preP.y;
// 修改控件的形变或者frame,center,就可以控制控件的位置
// 形变也是相对上一次形变(平移)
// CGAffineTransformMakeTranslation:会把之前形变给清空,重新开始设置形变参数
// make:相对于最原始的位置形变
// CGAffineTransform t:相对这个t的形变的基础上再去形变
// 如果相对哪个形变再次形变,就传入它的形变
self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);}
3 IOS中事件的产生和传递
3.1 事件的产生
· 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中(队列中FIFO,先产生的事件下执行)
· UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keywindow)。
· 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件的处理过程的第一步,找到合适的视图后,就会调用视图空间的touches方法来作为具体的事件处理。
事件的传递
· 触摸事件的传递是从父空间传递到子控件
UIApplication -> window -> 寻找处理时间的最合适的view
3.1.1 找到最合适的控件来处理事件
首先判断主窗口能否接受触摸事件
判断触摸点是否在自己身上
子控件数组中从后往前(子控件数组中最后一个元素开始)遍历子控件,重复前面的两个步骤(从后往前是为了让最上层的响应者最先响应时间)
找到一个view,将事件交给这个view,然后再遍历这个view的子控件,直到找到没有更合适的控件
如果没有符合条件的子控件,那就认为自己最合适处理这个时间,也就是自己最合适的view
UIView不能接受触摸事件的三种情况:
不允许交互---userInteractionEnable = NO;
隐藏---如果父控件隐藏,子控件也会隐藏,隐藏空间不能接受事件
透明度---如果设置一个空间的透明度 <= 0.01,会直接影响子控件的透明度。当透明度<= 0.01的时候,认为是透明
UIImageView默认不接受触摸事件,不允许交互,即userInteractionEnable = NO;若希望UIImageView可以交互,需要显式设置UIImageView的userInteractionEnable = YES;
3.2 顺一下流程
1.点击一个UIView或产生一个触摸事件A,这个触摸事件A会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
2.UIApplication会从事件对列中取出最前面的事件(此处假设为触摸事件A),把事件A传递给应用程序的主窗口(keyWindow)。
3.窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
如果想让某个view不能处理事件(或者说,事件传递到某个view那里就断了),那么可以通过刚才提到的三种方式。比如,设置其userInteractionEnabled = NO;那么传递下来的事件就会由该view的父控件处理。
例如,不想让蓝色的view接收事件,那么可以设置蓝色的view的userInteractionEnabled = NO;那么点击黄色的view或者蓝色的view所产生的事件,最终会由橙色的view处理,橙色的view就会成为最合适的view。
所以,不管视图能不能处理事件,只要点击了视图就都会产生事件,关键在于该事件最终是由谁来处理!也就是说,如果蓝色视图不能处理事件,点击蓝色视图产生的触摸事件不会由被点击的视图(蓝色视图)处理!
注意:如果设置父控件的透明度或者hidden,会直接影响到子控件的透明度和hidden。如果父控件的透明度为0或者hidden = YES,那么子控件也是不可见的!
3.3如何找到最适合的view
3.3.1
更多推荐
所有评论(0)