您的位置:首页 > 移动开发 > IOS开发

IOS事件传递及响应过程

2015-07-25 17:09 661 查看

IOS事件传递及响应过程

–>事件到来 –>事件分发 –>事件响应

事件(Events)

1.触摸事件(Touch Events)(单点触摸、多点触摸及各种手势)

2.晃动事件(Motion Events) (重力、加速度等传感器)

3.远程控制事件(Remote-Control Events) (线控耳机、airplay)

事件分发(Event Delivery)

触摸事件:触摸->硬件中断->UIKit封装成UIEvent对象(针对触摸事件)-> 当前运行的应用程序的事件队列 -> UIApplication ->key window ->Hit-Testing View

其他事件:key window -> First Responder

目的(官方文档)

The ultimate goal of these event paths is to find an object that can handle and respond to an event. Therefore, UIKit first sends the event to the object that is best suited to handle the event. For touch events, that object is the hit-test view, and for other events, that object is the first responder.

方法:

Hit-Testing Returns the View Where a Touch Occurred Touch

The hit-test view is given the first opportunity to handle a touch event. If the hit-test view cannot handle an event, the event travels up that view’s chain of responders as described in “ The Responder Chain Is Made Up of Responder Objects ” until the system finds an object that can handle it.

The Responder Chain Is Made Up of Responder Objects (Motion Or R-C)

事件处理的事件传递说明

发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中,UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)

UIView不接受触摸事件的三种情况:

1.不接收用户交互时:userInteractionEnabled = NO;

2.当视图隐藏时:hidden = YES;

3.当视图透明时:alpha = 0.0~0.01

注意:UIImageView的userInteractionEnabled默认是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的

事件传递的详细过程:

当触摸发生时,主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,但是这仅仅是整个事件处理过程的第一步,找到合适的视图控件后,就会调用视图控件的touches方法来做具体的事件处理

UIResponder

UIResponder内部提供了方法来处理事件;

1.触摸事件

(1)开始触摸:

- (void)touchesBegan:(NSSet * )touches withEvent:(UIEvent * )event


(2)触摸时移动:

- (void)touchesMoved:(NSSet * )touches withEvent:(UIEvent * )event


(3)触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程

- (void)touchesCancelled:(NSSet * )touches withEvent:(UIEvent * )event


(4)触摸结束:

- (void)touchesEnded:(NSSet * )touches withEvent:(UIEvent * )event


每个触摸事件的处理方法中,都有 NSSet *touches 和 UIEvent *event 两个参数;

1、一次完整的触摸过程,只会产生一个事件对象,4个触摸方法都是同一个event参数;

2、如果两根手指同时触摸一个view,那么view只会调用一次 touchesBegan:withEvent: 方法,touches参数中装着两个UITouch对象;

3、如果这两根手指一前一后分开触摸同一个view,那么view会分别调用两次 touchesBegan:withEvent:方法, 并且每次调用时的touches参数只包含一个UITouch对象;

4、根据touches中UITouch个数可以判断出使单点触摸还是多点触摸。

提示:touches中存放的都是UITouch对象。


2.晃动事件

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;


3. 远程控制事件

- (void)remoteControlReceivedWithEvent:(UIEvent *)event;


UITouch:

当用户用一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象;一根手指对应一个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;
/*
UITouchPhase是一个枚举类型,包含:
UITouchPhaseBegan(触摸开始)
UITouchPhaseMoved(接触点移动)
UITouchPhaseStationary(接触点无移动)
UITouchPhaseEnded(触摸结束)
UITouchPhaseCancelled(触摸取消)*/


UITouch的方法:

- (CGPoint)locationInView:(UIView *)view;


1> 返回值表示触摸在view上的位置;

2> 这里返回的位置是针对view坐标系的,(以view的左上角为原点(0,0));

3> 调用时传入的view参数为nil 的话,返回的是触摸点在UIWindow的位置。


- (CGPoint)previousLocationInView:(UIView *)view;
//该方法记录了前一个触摸点的位置;


UIEvent

每产生一个事件,就会产生一个UIEvent对象;

UIEvent:称为事件对象,记录事件产生的时刻和类型。


示例:



响应者链条

示例:



事件传递的完整过程

现将事件对象由上往下传递(由父控件传递给子控件),找到最合适的控件来处理这个事件。调用最合适控件的touches….方法 如果调用了[super touches….];就会将事件顺着响应者链条往上传递,传递给上一个响应者 接着就会调用上一个响应者的touches….方法

如何判断上一个响应者

如果当前这个view是控制器的view,那么控制器就是上一个响应者 如果当前这个view不是控制器的view,那么父控件就是上一个响应者

响应者链条的事件传递过程

如果view是控制器的view,就传递给控制器;如不是,则将其传递给它的父视图 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理 如果window对象也不处理,则其将事件或消息传递给UIApplication对象 如果UIApplication也不能处理该事件或消息,则将其丢弃

注意

为什么用队列管理事件,而不用栈?

队列先进先出,能保证先产生的事件先处理。栈先进后出。

代码展示:



//具体功能:
//1.每个视图都可以经过触摸拖动,并且在触摸时会有方法的动画效果在(touchesBegin方法中实现)。
//2.当触摸结束是会有放大还原的动画效果 在(touchesEnd方法中实现)。
//3.当触摸其中任意一个视图时,该视图会随着手势一起移动,并且当移动视图的中心点(view.center)进入其他两个视图的frame中时,会将两个视图吸附进来,最后看上去只剩下一个视图 在(touchesMove方法中实现)。
//4.当双击父视图的任意空白处时,三个视图将以动画的效果回到起始位置 在(touchesBegin方法中实现)
#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *magentaView;
@property (weak, nonatomic) IBOutlet UIImageView *cyanView;
@property (weak, nonatomic) IBOutlet UIImageView *yellowView;

@end

#define kMagentaViewFrame   (CGRect){53,55,100,100}
#define kCyanViewFrame      (CGRect){132,254,100,100}
#define kYellowViewFrame    (CGRect){223,457,100,100}

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

}
//自定义方法,求出触摸点的位置返回location
- (CGPoint)locationInTouches:(NSSet *)touches
{
// 0. 先取出触摸
UITouch *touch = [touches anyObject];

// 1. 返回触摸点(注意:坐标系 self.view)
return [touch locationInView:self.view];
}
//自定义方法,判断触摸点是否在三个视图的frame中,是返回YES,该视图随着手势一起移动。
- (BOOL)isImageViewsContainsPoint:(CGPoint)location
{
if (CGRectContainsPoint(_magentaView.frame, location)
|| CGRectContainsPoint(_cyanView.frame, location)
|| CGRectContainsPoint(_yellowView.frame, location)) {
return YES;
}

return NO;
}
//自定义方法,当进行双击时,三个视图回到原来的位置
- (void)resetFrames
{
[UIView animateWithDuration:0.5 animations:^{
_cyanView.frame = kCyanViewFrame;
_magentaView.frame = kMagentaViewFrame;
_yellowView.frame = kYellowViewFrame;
}];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

CGPoint location = [self locationInTouches:touches];

// 0. 先取出触摸
UITouch *touch = [touches anyObject];

if ([self isImageViewsContainsPoint:location]) { // 触摸发生在三个视图里
// 1. 取出触摸发生的视图
UIImageView *view = (UIImageView *)touch.view;

// 2. 设置触摸的视图的中心点为触摸点并放大触摸的视图
[UIView animateWithDuration:0.5 animations:^{
view.center = location;
view.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
} else { // 执行双击操作
if (touch.tapCount == 2) { // 双击
[self resetFrames];
}
}
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location = [self locationInTouches:touches];

// 移动时的触摸点落在哪个视图的矩形区域内,那个视图的中心点就设置为触摸点
if (CGRectContainsPoint(_magentaView.frame, location)) {
[UIView animateWithDuration:0.3 animations:^{
_magentaView.center = location;
}];
}

if (CGRectContainsPoint(_cyanView.frame, location)) {
[UIView animateWithDuration:0.3 animations:^{
_cyanView.center = location;
}];
}

if (CGRectContainsPoint(_yellowView.frame, location)) {
[UIView animateWithDuration:0.3 animations:^{
_yellowView.center = location;
}];
}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// 0. 先取出触摸
UITouch *touch = [touches anyObject];

// 1. 取出触摸发生的视图
UIImageView *view = (UIImageView *)touch.view;

// 还原触摸的视图
[UIView animateWithDuration:0.5 animations:^{
view.transform = CGAffineTransformIdentity;
}];
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: