触摸事件

1.简介

在UIResponder简介文章中我们介绍了UIResponder可以处理的事件以及他的派生类。

本章中我们将介绍如何去覆盖UIView的touch方法来创建一个划线的小应用。

 https://github.com/xufeng79x/XFTouchDemo

 

2.API介绍

1.一根手指或者多根手指触摸屏幕【有多少手指触摸了屏幕则会有多少UITouch对象在集合中】

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

2.一根手指或者多跟手指持续的在屏幕上滑动(随着滑动的持续,这个方法也会持续的被调用)【有多少手指在屏幕上移动则会有多少UITouch对象在集合中】  

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

3.一根手指或者多跟手指离开屏幕【有多少手指离开屏幕则会有多少UITouch对象在集合中】

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

4.当触摸正常结束前,事件被打断的情况下(电话等接入的情况等)【有多少手指还在屏幕上则会有多少UITouch对象在集合中】

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

 

在此例子中我们集成UIView,然后通过覆盖它的这些方法来取得控制权:

@interface XFDrowView : UIView

@end

 

需要注意的是:

1.任何UIResponder的派生类中要么覆盖全部方法,要么不要去覆盖任何方法。

   关于这一点的疑问是如果我对某个事件并不感兴趣,我并不想去处理怎么办,需要在派生类中调用父类方法,千万不要去直接向nextResponder发送消息。

- (void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
      [super touchesEnded:touches withEvent:event];
}

 

这里涉及到响应者链知识,我们知道UIResponder中有一个属性叫做nextResponder,它记录着当前view的父view,如果我们不想处理某个事件那么我们就直接使用super让

父类方法去处理,最终这个事件将会被传递当顶层的UIResponder父类方法中,在它的方法中将会向 nextResponder 发送消息。我想在UIResponder类中,它的实现应该是这样的(猜测的,看都不到源码)

 - (void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
     [nextResponder   touchesEnded:touches withEvent:event] }

2.不要去保存UITouch对象

当手指按下后,当前手指对应的UITouch对象实例将永不改变,随着手指的移动,UITouch对象的属性值将会被刷新。

当手指离开屏幕后,UITouch对象将会被销毁,整个一个过程都是系统控制的,在外部不需要去存储对象,更何况它的属性值是被系统不断刷新的。

 

3. touch事件与UIControl

UIControl是UIButton、UISlider等控件的父类,它的顶层基类也是UIResponder类,而UIControl类直接覆盖了touch方法,让所有集成自UIControl的类可以共享代码。同样我们也没有办法获取UIControl的源代码,都是猜测:(

这里我们以UIButton控件为例子,在为UIButton对象注册目标对象和目标方法的时候需要指定控件事件类型,换句话说需要指明怎样“摸”它才能相应目标对象的目标方法,如下代码:

[self.myButton addTarget:self action:@selector(touchme) forControlEvents:UIControlEventTouchUpInside];

 

上述代码为某一个UIButton对象设置了当UIControlEventTouchUpInside事件类型的时候才去触发事件处理方法。

可以推测UIControl内部有可能是以下面的逻辑来覆盖touchesEnded方法的:

// 触摸结束
- (void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 指针指向已经结束的触摸对象
    UITouch *touch = [touches anyObject];
    
    // 触摸结束时的位置(使用当前UIControl对象的坐标系)
    CGPoint touchLocation = [touch locationInView:self];
    
    // 判断结束时候是否在控件的bonds范围内
    if (CGRectContainsPoint(self.bounds, touchLocation)) {
        // 向所有注册了UIControlEventTouchUpInside事件的对象发送动作消息
        [self sendActionsForControlEvents: UIControlEventTouchUpInside];
    }else{
        // 触摸事件发生在bounds区域外,则向所有注册了UIControlEventTouchUpOutside事件的所有对象发送动作消息
        [self sendActionsForControlEvents: UIControlEventTouchOutInside];
    }
}

 

可以看到UIControl为我们封装好了方法,对外只暴露了事件类型供开发者使用,简单方便。

 

4.touch目标确定

我们在屏幕上点击某个控件的时候,第一落点便已经确定了目标控件,当我们移动手指离开第一落点控件,或者在第一落点控件外抬起手指,它的事件接受对象都是第一落点控件。

那么系统是如何确定第一落点控件的呢?查看API文档:https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/event_delivery_responder_chain/event_delivery_responder_chain.html#//apple_ref/doc/uid/TP40009541-CH4-SW1

 

在这里可以简单的做一下“翻译”工作,加深机制的了解。

 

1.任何事件(touch事件,motion事件,remote事件等)发生后,都进入当前活跃Application对象的事件队列中

2.Application对象将队列中的事件按顺序拿出后分发给Window对象,window对象会去寻找首个可能会处理该事件的对象,而该对象的寻找方式根据事件的不同

而不同:

  2.1如果是触摸事件,则将首个可能处理该事件的对象称作hit-test view,寻找他的过程称之为hit-testing。见hit-testing机制介绍

  2.2如果是motion或者remote事件,则将首个可能处理该事件的对象称作first responder,first responder作为响应链的首个响应对象。见响应链机制介绍

 

 

你可能感兴趣的:(触摸事件)