Funky、

Insist, Hard, Success

Welcome to Funky‘s zone. About me,so simple,Keep on coding,Keep on dancing. If you like something,stick to do!


OC-触摸事件&响应者链条

iOS中的事件

  • 在用户使用app过程中,会产生各种各样的事件,iOS中的事件可以分为3大类型:触摸事件,加速计事件,远程控制事件

响应者对象

  • 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。我们称之为响应者对象
  • UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件

UIResponder

  • UIResponder内部提供了以下方法来处理事件

    • 触摸事件

      // 一根或者多根手指开始触摸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;
      
    • 加速计事件

      - (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;
      

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;
    
  • UITouch的方法

    // 返回值表示触摸在view上的位置,调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
    - (CGPoint)locationInView:(UIView *)view;
    
    // 该方法记录了前一个触摸点的位置
    - (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;
    
        // 计算手指x轴偏移量
        CGFloat offsetY = curP.y - preP.y;
    
        // 修改控件的形变
        self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
    }
    

UIEvent

  • 每产生一个事件,就会产生一个UIEvent对象
  • UIEvent:称为事件对象,记录事件产生的时刻和类型
  • 常见属性

    // 事件类型
    @property(nonatomic,readonly) UIEventType     type;
    @property(nonatomic,readonly) UIEventSubtype  subtype;
    
    // 事件产生的时间
    @property(nonatomic,readonly) NSTimeInterval  timestamp;
    
    // UIEvent还提供了相应的方法可以获得在某个view上面的触摸对象(UITouch)
    

事件的产生和传递

  • 发生触摸事件后,会产生两的对象(UITouch,UIEvent),系统会将该事件加入到一个由UIApplication管理的事件队列(先进先出,先产生的事件先处理)中
  • UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常不是系统事件,先发送事件给应用程序的主窗口(keyWindow)
  • 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步
  • 找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理

注意: 如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件

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

  • 不接收用户交互 userInteractionEnabled = NO
  • 隐藏 hidden = YES
  • 透明 alpha = 0.0 ~ 0.01
    注意:UIImageView的userInteractionEnabled默认是NO,因此UIImageView以及它的子控件默认是不能接收触摸事件的

hittest方法和point方法

KeyWindow.m

// 点击白色view: 触摸事件 -> UIApplication -> [UIWindow hitTest] -> [whiteView hitTest] -> [ori hitTest] -> [green hitTest]

// 作用:寻找最合适view
// 只要一个事件传递给一个控件,就会调用这个控件的hitTest,返回谁,谁就是最合适view
// point:表示方法调用者坐标系上的点
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.判断下自己能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;

    // 2.判断下点在不在当前控件上
    if ([self pointInside:point withEvent:event] == NO) return  nil; // 点不在当前控件


    // 3.从后往前遍历自己的子控件
    // 1 0
    int count = (int)self.subviews.count;
    for (int i = count - 1; i >= 0; i--) {
        // 获取子控件
       UIView *childView = self.subviews[i];

        // 把当前坐标系上的点转换成子控件上的点
       CGPoint childP =  [self convertPoint:point toView:childView];

       UIView *fitView = [childView hitTest:childP withEvent:event];

        if (fitView) {
            return fitView;
        }

    }

    // 4.如果没有比自己合适的子控件,最合适的view就是自己
    return self;

}



// 判断下当前这个点在不在方法调用者上
// 使用注意点:点必须是方法调用者上的坐标系,才会判断准备
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    return NO;
}

触摸事件处理的详细过程

  • 用户点击屏幕后产生的一个触摸事件,经过一系列的传递过程后,会找到最合适的视图控件来处理这个事件
  • 找到最合适的视图控件后,就会调用控件的touches方法来作具体的事件处理
  • 这些touches方法的默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理

响应者链条

  • 是由多个响应者对象连接起来的链条
  • 作用:能很清楚的看见每个响应者之间的联系,并且可以让一个事件多个对象处理
  • 系统默认的touchBegan方法,会将响应者链条向上传递

事件传递的完整过程

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

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

响应者链的事件传递过程

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

最近的文章

OC/Swift-SQLite数据库

Demo地址SQLite_OC&amp;Swift_Demo地址 其中 SQLite_Swift_Demo是Swift3.0语言写的SQLite基础 SQLiteTool_OC_Demo是使用OC语言,利用runtime对SQLite进行的封装,面向模型类操作数据库 1. 数据库简介1.1 什 …

于  knowledge 继续阅读
更早的文章

Swift-基础语法&面向对象

Swift基础语法一、特色: 取消了预编译指令包括宏 取消了OC的指针及其他不安全的访问的使用 全面改为句点表示法 Swift3.0 去除了NS前缀 将绝大部分Class转化为struct @UIApplication 程序的入口 只有.swift 没有 .h/.m 在swift中默认全局共享 …

于  knowledge 继续阅读
comments powered by Disqus