iOS开发——响应链(Responder Chain)的深入理解和代码示例

我在之前一篇博客《iOS响应者链Responder Chain浅析》中对iOS开发中遇到的响应者链概念有了基本的了解。但是仅仅停留在理解概念的基础上还是远远不够的。该博客我们会通过代码案例来深入理解响应链。该博客的示例上传至 https://github.com/chenyufeng1991/ResponderChain  。

(1)首先来说说第一响应者(First Responder)。响应事件的传递过程就是为了找到第一响应者。以下几个方法:

isFirstResponder:判断该View是否为第一响应者。

canBecomeFirstResponder:判断该View是否可以成为第一响应者。

becomeFirstResponder:使该View成为第一响应者。

resignFirstResponder:取消View的第一响应者。

由于输入框(UITextField)是研究第一响应链这非常方便的View,所以这里使用输入框做一个demo。

(2)界面中有两个输入框,分别设置tag为101,102,因为给View设置不同的tag可以用来区分不同的View。光标在哪一个输入框中,那么该输入框就是第一响应者。通过以下方法来找到第一响应者:

设置界面中的View tag:

self.view.tag = 100;
self.nameTextField.tag = 101;
self.passwordTextField.tag = 102;

通过遍历界面中的所有View,来找到第一响应者:

// 找到当前页面中谁是第一响应者
- (IBAction)whoIsFirstResponder:(id)sender
{
    if ([self.view isFirstResponder])
    {
        [self showDialog:nil message:[NSString stringWithFormat:@"第一响应者是%ld",(long)self.view.tag]];
        return;
    }

    for (UIView *view in self.view.subviews)
    {
        if ([view isFirstResponder])
        {
            [self showDialog:nil message:[NSString stringWithFormat:@"第一响应者是%ld",(long)view.tag]];
            return;
        }
    }
}

假设你把光标放在tag=101的输入框中,demo的演示效果如下:

(3)在我们的实际开发中,有这样的需求,可能界面中有多个输入框,但是我们要设置某个输入框为当前的输入,这就可以通过设置第一响应者来完成。下面的代码就是设置输入框101为当前的输入框:

// 设置nameTextField 101为第一响应者
- (IBAction)setNameToFirstResponder:(id)sender
{
    if (![self.nameTextField isFirstResponder])
    {
        if ([self.nameTextField canBecomeFirstResponder])
        {
            [self.nameTextField becomeFirstResponder];
            [self showDialog:nil message:@"设置101为第一响应者"];
        }
    }
}

实现效果如下:

(4)跟上面(3)的需求类似,我们可能需要取消输入框的第一响应者,可以使用如下代码:

//取消nameTextField 101为第一响应者
- (IBAction)cancelNameFromFirstResponder:(id)sender
{
    if ([self.nameTextField isFirstResponder])
    {
        [self.nameTextField resignFirstResponder];
        [self showDialog:nil message:@"取消101为第一响应者"];
    }
}

实现效果如下:

(5)其实我想要知道,该界面的主View(self.view)是否能成为第一响应者?测试代码如下:

//设置self.view为第一响应者
- (IBAction)setSelfViewToFirstResponder:(id)sender
{
    if ([self.view canBecomeFirstResponder])
    {
        [self.view becomeFirstResponder];
        if ([self.view isFirstResponder])
        {
            [self showDialog:nil message:@"设置self.view为第一响应者成功"];
        }
        else
        {
            [self showDialog:nil message:@"设置self.view为第一响应者失败"];
        }
    }
    else
    {
        [self showDialog:nil message:@"不能设置self.view第一响应者"];
    }
}

结果如下:

可以发现,self.view不能成为第一响应者。

(6)同时我也想要知道按钮能否成为第一响应者,也可以使用类似的代码如下:

//判断按钮能否成为第一响应
- (IBAction)buttonCanBecomeFirstResponder:(id)sender
{
    if ([self.judgeThisButton canBecomeFirstResponder])
    {
        [self.judgeThisButton becomeFirstResponder];
        if ([self.judgeThisButton isFirstResponder ])
        {
            [self showDialog:nil message:@"设置该按钮为第一响应者成功"];
        }
        else
        {
            [self showDialog:nil message:@"设置该按钮为第一响应者失败"];
        }
    }
    else
    {
        [self showDialog:nil message:@"不能设置该按钮为第一响应者"];
    }
}

结果如下:

结果发现:按钮也不能成为第一响应者。如果想要测试其他View的该特性,就可以使用以上代码。

(7)对于某一次的点击或者触摸操作,我们来打印出它的响应链。主要用到的方法是nextResponder.我这里通过点击按钮,看Log日志。

代码实现如下:

//获得整个触摸事件的响应链
- (IBAction)getNextResponder:(id)sender
{
    UIResponder *responder = sender;
    NSLog(@"\n%@\n",[responder class]);
    while (responder != nil)
    {
        responder = [responder nextResponder];
        NSLog(@"\n%@\n",[responder class]);
    }
}

Log打印出的结果如下:

这里可以看到响应链传递顺序为:UIButton-->UiView-->FourthViewController-->UITabBarController-->UITransitionView-->UIWindow-->UIApplication-->AppDelegate.  输出结果和我们概念中讲到是一样的。

(8)如果我们想要在响应链传递到树中某个节点的时候,去执行一定的操作,可以执行如下代码:

- (IBAction)captureEvent:(id)sender
{
    UIResponder *responder = sender;
    while (responder != nil)
    {
        if ([responder isKindOfClass:[self class]])
        {
            NSLog(@"事件传递到该类VC,可以执行相应操作");
        }

        if ([responder isKindOfClass:[UIApplication class]])
        {
            NSLog(@"事件传递到UIApplication,可以执行相应操作");
        }
        responder = [responder nextResponder];
    }
}

输出结果如下:

(9)在触摸事件中,有touchesBegan,touchesCancelled。。。这几个方法。这些方法是UIResponder的实例方法,而界面上可以交互的View和VC,基本都是继承自UIResponder,所以也会有touchesBegan方法。当我们在AppDelegate.m和VC中都重写该方法后,通过触摸屏幕来测试,发现响应链传递到VC后,就默认不再往AppDelegate传递了。如要调用AppDelegate,可以使用super进行显式调用即可。

小结下,使用响应链,能够让一条链上的多个对象对同一事件做出响应。每一个应用有一个响应者链,我们的视图结构是一个N叉树(一个视图可以有多个子视图,一个子视图同一时刻只有一个父视图),而每一个继承自UIResponder的对象都可以在这个N叉树中成为一个节点。当叶节点成为最高响应者的时候,从这个叶节点开始往其父节点开始追溯出一条链,那么对于这一个叶节点来讲,这一条链就是当前的响应者链。响应者链将系统捕获到的UIEvent与UITouch从叶节点层层向上分发,期间可以选择停止分发,也可以继续向上分发。一句话就是事件的传递过程。

时间: 2024-10-20 10:11:10

iOS开发——响应链(Responder Chain)的深入理解和代码示例的相关文章

iOS开发——响应链(Responder Chain)的深入理解和代码示例(二)

响应链机制是开发中很重要的概念,在一些事件的处理中需要对响应链的传递有深入的了解,我们才能对事件的传递有更好的控制.今天我们继续来研究下响应链,并实现一个很简单的功能.示例代码已经上传至 https://github.com/chenyufeng1991/HitTest ,可以进行下载调试.要实现的一个很简单的功能就是:透过顶部视图,让底部视图来响应点击事件,这也会响应链使用中非常重要的应用.下面也会涉及一些不同的案例.用到最常用的方法为hitTest:withEvent和pointInside

ios中事件的响应链(Responder chain)和传递链

事件的响应链涉及到的一些概念 UIResponder类,是UIKIT中一个用于处理事件响应的基类.窗又上的所有事件触发,都由该类响应(即事件处理入又).所以,窗又上的View及控制器都是 派生于该类的,例如UIView.UIViewController等. 调用UIResponder类提供的方法或属性,我们就可以捕捉到窗又上的所有响应 事件,并进行处理. 响应者链条是由多个响应者对象连接起来的链条,其中响应者对象是能处理事 件的对象,所有的View和ViewController都是响应者对象,利

iOS开发中GCD在多线程方面的理解

GCD为Grand Central Dispatch的缩写. Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法.在Mac OS X 10.6雪豹中首次推出,并在最近引入到了iOS4.0. GCD是一个替代诸如NSThread等技术的很高效和强大的技术.GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题. GCD可以完成很多事情,但是这里仅关注在iOS应用中实现多线程所需的一些基础知识. 在开始之前,需要理解是要提供给GCD队列的是代

iOS事件响应链

@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css); 事件传递之响应链 当你设计App时你可能需要动态的响应事件.例如,一个触摸事件可能发生在屏幕上不同的对象中,你需要决定哪个对象来响应这个给定的事件,理解对象如何接收事件. 当用户触发的一个事件发生,UIKit会创建一个包含要处理的事件信息的事件对象.然后她会将事件对象

【iOS开发系列】用简单工厂模式理解OC反射机制

// 在iOS开发中,简单工厂模式使用得并不多.但是.我认为这是OC反射机制很好的一个例子, // 所以本文将以计算器为例,讲解简单工厂模式和OC的反射机制. // [简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类( // 这些产品类继承自一个父类或接口)的实例.该模式中包含的角色及其职责:工厂角色.抽 // 象产品角色.具体产品角色] // --百度百科 简单工厂模式 // 上面这句话可能不怎么好理解,我在网上找到了一个例子,可能例子本身不能完全解释这个 // 设

【iOS开发-50】利用创建新的类实现代码封装,从而不知不觉实践一个简单的MVC实验

接上次案例谈代码封装.上次案例见:[iOS开发-48]九宫格布局案例:自动布局.字典转模型运用.id和instancetype区别.xib重复视图运用及与nib关系 代码封装的原则是:要保证视图控制器尽量少的接触到其他对象的属性,也就是说,尽量把数据或者属性封装到一个类里面,然后利用类或者对象的方法来调用或者设置数据.而是赤裸裸地把属性都写在视图控制器中.核心作用在于:减少视图控制器的代码量,把数据和属性的处理封装起来,这样也便于其他视图控制器的使用. 要做到的结果就是如下(我们要根据数组里面的

iOS开发 - 响应者链触摸事件

触摸事件 在用户使用app过程中,会产生各种各样的事件 iOS中的事件可以分为3大类型 触摸事件: 加速计事件: 远程控制事件: 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为"响应者对象" UIApplication.UIViewController.UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件 UIResponder UIResponder内部提供了以下方法来处理事件

iOS开发响应者链触摸事件

触摸事件 在用户使用app过程中,会产生各种各样的事件 iOS中的事件可以分为3大类型 触摸事件: 加速计事件: 远程控制事件: 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为“响应者对象” UIApplication.UIViewController.UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件 UIResponder UIResponder内部提供了以下方法来处理事件 触摸事件

iOS开发:集成支付宝(遇见的坑和便捷撸代码)

开发iOS最重要的就是支付了,天朝之内最常用的就是支付宝了,下面就以自己的经历说明如何集成支付宝+遇见的坑. 首先,集成支付宝最好别使用Cocoapods,很多人都说使用起来很方便,可是我每次只要使用Cocoapods导入支付宝SDK,总是出现各种莫名其妙的错误,并且还每次都不一样,最终,我只能手动导入. 其实可以使用ping++和其他更为方便.如 http://winann.blog.51cto.com/4424329/1601654 https://www.pingxx.com/ 以自己集成