美文网首页iOS 实用技术iOS开发技巧ios
最好的输入方式:iOS中的触摸事件

最好的输入方式:iOS中的触摸事件

作者: 皮皮Warrior | 来源:发表于2016-11-21 02:42 被阅读1184次

乔帮主在发布会上提到,用户的手才是最好的输入设备,的确,iPhone之后,非触屏手机再已难觅。触摸是最基本的用户输入事件,理解iOS特有的触摸事件响应机制,能够良好管理程序中触摸响应方法,避免冲突的发生。

iOS中的事件

iOS中的事件主要分为三类:

  1. UIControl Actions: 使用target/action注册的SEL。
  2. User Events: 用户与应用之间的交互:触摸,输入文字,摇晃,远程控制等。
  3. System Events: 应用启动,切前后台,低内存等。
    cocoa和cocoa touch的程序启动后,,会首先初始化一些基本资源:在主线程创建一个main event loop;初始化主UIWindow
    应用启动过程-w500
    main event loop本质上是一个NSRunLoop,与其他辅助线程的run loop不同,其是自创建后自动开始运行的。主消息循环最大的特点是:它在创建时就与负责捕获用户事件的系统底层建立了连接,所以它的input source可以收到系统传递过来的用户事件。UIApplication对象会将当前要处理的用户事件封装成UIEvent,发送给UIWindow,在由UIWindow转发给对应的响应者。
    iOS的触摸事件响应机制

    hit-testing流程

    iOS中hit-testing使用逆前序的深度遍历算法来确定用户点按的最低层级(最靠近用户)的视图,该hitTest视图是触摸事件的响应链头结点。
    逆前序的深度遍历算法:根节点-->右子树-->左子树。
    当收到触摸事件后,UIApplication在当前视图层级中,从key window开始(最顶级),从上往下遍历子视图调用hitTest:withEvent:,若找到hitTest视图则停止遍历并返回。
    当视图收到hitTest:withEvent:方法后,通过下列条件判断是否在该视图执行hit-testing。

    1. pointInside:withEvent:方法返回YES。pointInside:withEvent:方法用来判断触摸点是否在当前视图内。
    2. hidden == NO。
    3. userInteractionEnabled == YES。
    4. alpha >= 0.01。若view的content绘制为透明的,则不受影响。

    需要注意的是,当clipsToBounds == NO时,视图的子视图可能会超出其bounds,这种情况如果触摸点在子视图超出父视图的范围,那么hit-tesing不会再此视图树上执行。

    hit-testing
    如图,当用户触摸viewB.1时,UIApplication对象收到触摸事件,从key window开始执行hit-testing,首先访问viewC,由于pointInside:withEvent:方法返回NO,取消执行并访问viewB,满足执行,则从右往左开始访问其子视图(视图层级从下往上),找到viewB.1,它没有子视图,则返回自己。最终UIWindow对象将viewB.1作为hitTest视图返回给UIApplication对象。
    hit-testing流程图
    可以看到,当某一视图收到hitTest:withEvent:方法后,它会向所有子视图发送hitTest:withEvent:方法,若它的没有子视图或所有子视图返回nil,那么就返回自己,所有hit-testing流程最终一定会找到一个对象UIView/UIWindow去接收触摸事件。
    以下是hitTest:withEvent:可能的实现。
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
            return nil;
        }
        if ([self pointInside:point withEvent:event]) {
            for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
                CGPoint convertedPoint = [subview convertPoint:point fromView:self];
                UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
                if (hitTestView) {
                    return hitTestView;
                }
            }
            return self;
        }
        return nil;
    }
    

    responder chain

    responder chain是UIResponder对象组成的链形结构,它以first responder为头结点,UIApplication对象为尾节点,事件从头开始在响应链中向上传递。
    UIResponder用来设计处理事件,UIApplication, UIViewController, UIView都是其子类,只要它们实现了UIResponder中的钩子方法,就可以响应对应的事件。

    UIResponder的继承关系UIResponder的继承关系
    其中first responder用来第一个接触事件,可以使用becomeFirstResponder来设置它,主要要在视图层级已经完全建立之后再设置。

    If you try to assign the first responder in viewWillAppear:, your object graph is not yet established, so the becomeFirstResponder method returns NO

    默认情况下,fist responder是当前UIWindow中最有可能响应事件的UIView,这由UIkit决定。
    iOS中大部分的事件都依赖响应链来找到最终的响应者,在UIResponder的头文件中可以看到,Touch events,Motion events,Remote events,UIControl Action,Text editing,press events等事件都可以在响应链中传递。

    寻找响应对象

    UIApplication在处理的事件时,触摸事件会交给hitTest view开始的响应链处理,其他的动作事件,远程事件,系统事件等,会交给first responder开始的响应链处理。
    UIKit会将用户事件发送给理论上最合适的对象。所以当程序中的响应者要经过很长的查找路径时,这时就要考虑是否实现是否设计合理了。

    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-test视图获得了最先接受触摸对象的机会,但如果它不能处理对应的触摸事件,那么UIKit会沿着以hit-test开头的响应链寻找能够最终的响应者。


    The responder chain on iOSThe responder chain on iOS

    当找到响应者或已经到链尾(UIApplication)仍不能处理,UIKit会停止查找,对于后者,对应的事件会被丢弃。

    除了UIResponder对象,UIGestureRecognizerUIControl也可以响应触摸事件,但它们参与触摸事件响应的方式不同。

    1. UIGestureRecognizer在响应链中的位置取决于依附的视图。
    2. UIControl参与响应的方式决定于其关联的target。
      UIGestureRecognizer要先于视图收到触摸事件,但需要注意的是,若该视图也可以响应触摸事件(实现了UITouch生命周期函数),那么手势对象并不会阻碍视图的响应,双方是同时响应的,只不过存在先后顺序。
      UIGestureRecognizer与UIView的接触事件的次序UIGestureRecognizer与UIView的接触事件的次序

    响应触摸事件

    当确定了响应链后,UIWindow会向hitTest View发送以下方法:

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

    这是UIResponder用于响应触摸事件的方法,这些钩子方法的默认实现是向nextResponder转发方法。
    当触摸事件在响应链上传递时,判断当前UIResponder能否响应的条件是:其是否实现了touchesBegan方法。
    在这些UITouches序列的生命周期方法中,我们可以获取对应UIEventUITouch,利用它们所提供的信息,进一步决定如何响应用户的触摸事件。

相关文章

  • 最好的输入方式:iOS中的触摸事件

    乔帮主在发布会上提到,用户的手才是最好的输入设备,的确,iPhone之后,非触屏手机再已难觅。触摸是最基本的用户输...

  • iOS 事件的传递响应机制

    iOS 中的事件 触摸事件 加速计事件 远程控制事件 iOS 中的触摸事件1、触摸事件发生,操作系统iOS会将此类...

  • cocos2d第五篇--用户事件

    在移动平台中用户输入的方式有触摸屏幕,键盘输入和各种传感器。这些用户输入被封装成为事件,列如,在iOS平台有触摸事...

  • iOS 点击事件传递及响应

    1.iOS中的事件 iOS中的事件可以分为3大类型: 触摸事件加速计事件远程控制事件这里我们只讨论iOS中的触摸事...

  • UI进阶12 触摸事件

    触摸事件 iOS中的事件 在用户使用app过程中,会产生各种各样的事件 iOS中的事件可以分为3大类型:触摸事件,...

  • iOS事件传递与响应原理

    iOS 中的事件可以分为3大类:触摸事件、加速计事件、远程控制事件,本文仅以 iOS 中的触摸事件为例进行讨论,主...

  • EditText及输入法使用技巧总结

    日常开发中,主要的输入事件方式有触摸事件和系统按键外以及输入法输入文字等。现在就来总结下EditText和输入法的...

  • iOS事件

    概述 iOS中事件有触摸事件、加速计事件、远程控制事件,下面以触摸事件为例研究下iOS事件相关的内容 UIResp...

  • iOS事件的响应者链

    iOS 事件响应者链 1 iOS中的事件 触摸事件 加速计事件 远程控制事件 在iOS中不是任何对象都能处理事件,...

  • 17·iOS 面试题·描述一下触摸事件传递流程

    前言 在 iOS 中,常见的事件有:触摸事件、加速计事件、远程控制事件等。在这里我们主要讨论触摸事件,对于触摸事件...

本文标题:最好的输入方式:iOS中的触摸事件

本文链接:https://www.haomeiwen.com/subject/gaaypttx.html