ios中的事件处理、响应者链条以及第一响应者

在ios中,事件UIEvent类来表示,当一个事件发生时,系统会搜集的相关事件信息,创建一个UIEvent对象,最后将该事件转发给应用程序对象(UIApplication)。日常生活中,主要有三种类型的事件:触摸事件,加速计事件以及远程遥控事件。下面是官方的一张图片:

当用户通过以上方式触发一个事件时,会将相应的事件对象添加到UIApplication的事件队列中。UIApplication会循环的从队列中拿出第一个事件来处理。首先将该事件分发给UIApplication 的主窗口对象(KeyWindow),然后由主窗口决定如何将事件交给最合适的响应者(UIResponder)来处理取决于事件的类型。这里主要分两种情况:

  1、触摸事件:UIApplication通过一个触摸检测来决定最合适来处理该事件的响应者,一般情况下,这个响应者是UIView对象。

  2、加速计事件或远程遥控事件:UIApplication寻找UIWindow中的第一响应者。找到第一响应者(The First Responder)后,会将该事件对象派发给该响应者以便处理。

下面分别讨论上述两种情况。

一、触摸事件中的触摸检测

  首先我们需要明确一个UIView对象能够接收触摸事件至少要保证以下三个条件:

  1、userInteractionEnabled属性为YES,该属性表示允许控件同用户交互。

  2、Hidden属性为NO。控件都看不见,还触摸啥?

  3、opacity属性值0 ~0.01,不能透明过分了吧?

  接下来的我们仅仅认为该三个基本属性都满足要求,方便描述,当然对于不满足要求的自然是不能接收触摸说事件的。

  当用户手指触摸到屏幕中的某一块区域时,UIWindow查找其子控件,然后通过调用所有自控件的方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

  来通过指定的触摸点获取最合适的UIView来处理该触摸事件。如何通过触摸点获取UIView原理其实非常简单,只需要检查该触摸点是否在该控件所在的矩形区域内就可以了,其实hitTest:withEvent方法内部也是调用方法:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

  如果检测到传入的控件包含该触摸点就返回YES。

  当通过hitTest方法检测获取到UIView后,会继续对该UIView对象做一次检测操作,也就是查找subViews的subViews做触摸检测。最终该方法会返回一个最合适的控件来响应该事件。再次申明,如果之前的三个条件不满足,那么该UIView以及其subViews都不可以响应该触摸事件。

  找到响应者后,响应者可以重写以下方法来对触摸事件做响应:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];//让下一个响应者可以有机会继续处理
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
}

  在响应方法内部,我们也可以将这个触摸事件继续传递给父控件的对应方法处理。然后父控件还可以将该事件继续向上传递,直到传递给UIApplication对象。这一系列的响应者对象就构成了一个响应者链条。

二、第一响应者 (The First Responder)

  什么是第一响应者?简单的讲,第一响应者是一个UIWindow对象接收到一个事件后,第一个来响应的该事件的对象。注意:这个第一响应者与之前讨论的触摸检测到的第一个响应的UIView并不是一个概念。第一响应者一般情况下用于处理非触摸事件(手机摇晃、耳机线控的远程空间)或非本窗口的触摸事件(键盘触摸事件),通俗点讲其实就是管别人闲事的响应者。在IOS中,当然管闲事并不是所有控件都愿意的,这么说好像并不是很好理解,或着是站在编程人员的角度来看待这个问题,程序员负责告诉系统哪个对象可以成为第一响应者(canBecomeFirstResponder),如果方法canBecomeFirstResponder返回YES,这个响应者对象才有资格称为第一响应者。有资格并不代表一定可以成为第一响应者,就好像符合要求并不一定能够应聘成功一样,所以还差一个聘用环节,那就是becomeFirstResponder正式成为第一响应者。

  请原谅我的这些可能不太正常的想法,个人感觉上面的过程又有点像招聘流程,简历筛选就是canBecomeFirstResponder,becomeFirstResponder就是正式成为公司的员工。那么既然公司由聘用,那么对应的就有辞退咯!对应的方法就是canResignFirstResponder,这个表示第一响应者是否可以被辞退,有些牛逼到逆天的员工并不是说辞退就辞退的,争取有一天可以成为这个逆天员工,好吧,我又扯远了。还有一个方法就是resignFirstResponder,正式辞退该员工。

  值得注意的是,一个UIWindow对象在某一时刻只能有一个响应者对象可以成为第一响应者。我们可以通过isFirstResponder来判断某一个对象是否为第一响应者。

  大家先看下面的一个手机界面:

  

  界面中包含两个输入框,一个切换第一响应者的按钮。我为两个输入框绑定了开始编辑事件,然后在事件中打印第一响应者相关的信息,代码如下:

NSString * NSStringFromBoolValue(BOOL boolValue){
    return boolValue ? @"YES" : @"NO";
}

- (IBAction)editingBegin:(id)sender {
    NSLog(@"top : 是否可以成为第一响应者=>%@,是否第一响应者=>%@",NSStringFromBoolValue(self.topInputView.canBecomeFirstResponder),NSStringFromBoolValue(self.topInputView.isFirstResponder));
    NSLog(@"down : 是否可以成为第一响应者=>%@,是否第一响应者=>%@",NSStringFromBoolValue(self.downInputView.canBecomeFirstResponder),NSStringFromBoolValue(self.downInputView.isFirstResponder));
}

  当点击第一个输入框时,打印如下:

  

  我们可以看到两个输入框都可以成为第一响应者。但是只有第一个输入框才是第一响应者。

  当点击第二个输入框时,打印如下:

  

  我们可以看到两个输入框都是第一响应者,但是只有下面那个输入框才是第一响应者。

  我们注意到,两个输入框的下方有一个按钮用于切换第一响应者。按钮的响应事件方法为:

  

- (IBAction)switch:(id)sender {
    /**
     *  1、如果顶部输入框是第一响应者就将第一响应者切换为下方的输入框
        2、如果顶部输入框不是第一响应者,就将其设置为第一响应者。
     */
    if(self.topInputView.isFirstResponder){
        [self.downInputView becomeFirstResponder];
    }else{
        [self.topInputView becomeFirstResponder];
    }

    NSLog(@"父控件中的第一响应者:%@",[self.uiview findFirstResponder]);

    NSLog(@"top : 是否可以成为第一响应者=>%@,是否第一响应者=>%@",NSStringFromBoolValue(self.topInputView.canBecomeFirstResponder),NSStringFromBoolValue(self.topInputView.isFirstResponder));
    NSLog(@"down : 是否可以成为第一响应者=>%@,是否第一响应者=>%@",NSStringFromBoolValue(self.downInputView.canBecomeFirstResponder),NSStringFromBoolValue(self.downInputView.isFirstResponder));

}

  在点击了第一个输入框后,我们点击切换第一响应者,屏幕打印如下:

  我们可以看到,这个时候下方的输入框成为第一响应者,并且触发了开始编辑事件,所以有两次打印。切换后,焦点也切换到第二个输入框中,我们通过键盘输入时,内容会在第二个输入框中出现。

  时常有人碰到希望点击空白区域,隐藏键盘的问题。例如输入框输入一半,觉得不想再编辑了,可以点击空白区域来隐藏键盘,这个时候其实只需要告诉系统,这个第一响应者的位置我不想要了,我想辞职,这就够了!下面是点击第一个输入框后,然后点击空白区域,触发touchesBegin事件,最后topInputView辞职的过程,通过这种方式就可以隐藏键盘了。代码如下:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.topInputView resignFirstResponder];
}

  

  今天就先写到这里,后续会补充一些细节性的东西。

  

 

时间: 2024-10-24 13:49:00

ios中的事件处理、响应者链条以及第一响应者的相关文章

iOS中的事件处理

前言:iOS中事件处理,是一个很重要也很难得地方.涉及到响应者链的地方的面试题,很多工作两三年的老鸟也未必能回答的很专业.这里详细介绍一下iOS中的事件处理,以及响应者链. 1. 三大事件 触摸事件 加速计时间 远程控制事件 2. 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为 响应者对象 UIApplication.UIViewController.UIView都继承自UIResponder,因此它们都是响应者对象,都能够接

iOS开发中的事件处理(二)-事件传递,响应者链条-

转载请注明出处:http://blog.csdn.net/whjForWork/article/details/44926833 事件处理的事件传递 简介: 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件 队列中,UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow) UIView不接受触摸事件的三种情况: 不接收用户交互 userInteractionEnabled = NO

iOS中的事件处理--续篇

1. 图解iOS事件处理的流程 1.当触摸屏幕的时候,由操作系统捕获响应的消息,并且将消息放进UIApplication管理的队列中. 2.从UIApplication中取出消息交给UIWindow,UIWindow做出以下判断: 判断自己能不能接收事件 点在不在自己窗口上 遍历自己的子控件,找比自己更合适的View 子控件接收,继续上面的步骤:子控件不接收,窗口自己处理事件 3.直到找到最合适的yellowView,此时事件传递结束,然后反向执行响应者链条. 4.touch的默认做法:自己不处

iOS开发--响应者链条

简单来说就是 :一级一级的找到响应的视图,如果没有就传给UIWindow实例和UIApplication实例,要是他们也处理不了,就丢弃这次事件... 对于IOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕.晃动设备.通过遥控设施控制设备.对应的事件类型有以下三种: 1.触屏事件(Touch Event) 2.运动事件(Motion Event) 3.远端控制事件(Remote-Control Event) 响应者链条概念: iOS系统检测到手指触摸(Touch)操作时会将其打包成一个U

事件处理以及响应者链条

在ios中,事件UIEvent类来表示,当一个事件发生时,系统会搜集的相关事件信息,创建一个UIEvent对象,最后将该事件转发给应用程序对象(UIApplication).日常生活中,主要有三种类型的事件:触摸事件,加速计事件以及远程遥控事件.下面是官方的一张图片: 当用户通过以上方式触发一个事件时,会将相应的事件对象添加到UIApplication的事件队列中.UIApplication会循环的从队列中拿出第一个事件来处理.首先将该事件分发给UIApplication 的主窗口对象(KeyW

iOS开发UI篇—响应者链条

iOS开发UI篇—响应者链条 一.触摸事件处理的详细过程 用户点击屏幕后产生的一个触摸事件,经过一些列的传递过程后,会找到最合适的视图控件来处理这个事件 找到最合适的视图控件后,就会调用控件的touches方法来作具体的事件处理 touchesBegan… touchesMoved… touchedEnded… 这些touches方法的默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理 二.响应过程 一次完整的触摸事件的传递响应的过程 UIAppliction --> UIW

ios响应者链条

一次完整的触摸事件的传递响应的过程 UIAppliction --> UIWiondw -->递归找到最适合处理事件的控件 控件调用touches方法-->判断是否实现touches方法-->没有实现默认会将事件传递给上一个响应者-->找到上一个响应者 1.响应者链条: 由很多响应者链接在一起组合起来的一个链条称之为响应者链条 什么是响应者:继承UIResponder的对象称之为响应者对象 2.默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理 如何判断当

iOS中的触摸事件

1.响应者对象 iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件,我们称之为“响应者对象”. UIApplication.UIViewController.UIView都继承自UIResponder,因此他们都是响应者对象,都能够接收并处理事件. 2.UIResponde UIResponder内部提供了以下方法来处理事件 触摸事件: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)e

ios中UIControl详解

上篇讲到了UITouch和UIEvent事件,简单回顾一下,UIEvent是一系列UITouch的集合,在IOS中负责响应触摸事件.另外还提到了响应者链的概念,在IOS中,所有事件有一个最先响应者,事件可以沿着响应者链向下传递. 接下来是UIControl对象 UIControl是UIView的子类,当然也是UIResponder的子类.UIControl是诸如UIButton.UISwitch.UITextField等控件的父类,它本身也包含了一些属性和方法,但是不能直接使用UIControl