区块链技术博客
www.b2bchain.cn

iOS面试题目解析05- 事件传递和响应者链求职学习资料

本文介绍了iOS面试题目解析05- 事件传递和响应者链求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

事件的生命周期大概为三个阶段:

  1. 事件的产生
  2. 事件的传递
  3. 事件的响应

事件的产生

苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()

当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。

事件的分类

iOS中的事件可以分为3大类型:

  • 触摸事件
  • 加速计事件
  • 远程控制事件

这里只关注触摸事件。

在iOS中,并不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,比如:

  • UIApplication
  • UIViewController
  • UIView

在UIResponder中提供了以下方法来处理触摸事件:

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

对于触摸事件,iOS中提供了UITouch对象对象,其作为用:

  • 保存着跟手指相关的信息,比如触摸的位置、时间、阶段
  • 当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
  • 当手指离开屏幕时,系统会销毁相应的UITouch对象

比如在UIView的几个touch方法中,就是将UITouch作为参数传递进去的:

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

UITouch的产生为:

当用户用一根手指触摸屏幕时,会创建一个与手指相关的UITouch对象,并且一根手指对应一个UITouch对象。如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象;如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象。

事件的传递

  • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中。
  • 然后UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
  • 主窗口keyWindow会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。

找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。

也就是说,触摸事件的传递是从父控件传递到子控件,即UIApplication->window->寻找处理事件最合适的view的一个过程。

寻找合适的控件来处理事件

  1. 首先判断主窗口(keyWindow)自己是否能接受触摸事件
  2. 判断触摸点是否在自己身上
  3. 子控件数组中从后往前遍历子控件,重复前面的两个步骤
  4. 找到view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
  5. 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件。

比如下面的四个View:白色、红色、蓝色、黄色,其中白色view是UIViewController的view,点击黄色view事件的传递过程为:

UIApplication -> UIWindow -> 白色view(UIViewController的view) -> 红色view -> 蓝色view -> 黄色view

iOS面试题目解析05- 事件传递和响应者链

找到合适的控件依赖于hitTest:withEvent:pointInside:withEvent:两个方法:

// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;  // default returns YES if point is in bounds - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   

hitTest:withEvent: 方法

  • 作用:寻找并返回最合适的view。
  • 调用时机:只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法。不管这个控件能不能处理事件,也不管触摸点在不在这个控件上,事件都会先传递给这个控件,随后再调用hitTest:withEvent:方法
- (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. 从后往前遍历子控件数组     for (int i = (int)self.subviews.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) { // 如果能找到最合适的view             return fitView;         }     }      // 4. 没有找到更合适的view,自己作为最合适处理这个事件的view。     return self; }

iOS面试题目解析05- 事件传递和响应者链

pointInside:withEvent: 方法

pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件。

事件的响应

事件从UIApplication->UIWindow->寻找处理事件最合适的view传递后,就会调用控件的touches方法来作具体的事件处touchesBegan…touchesMoved…touchedEnded…等。touches 方法就是对事件的响应,事件的响应是顺着响应链向上传递的,这个传递是依赖于UIResponder的nextResponder:

  • UIView :如果view是VC的root view,则它的nextResponder是 VC;否则是父view
  • UIViewController :如果 vc 是window的root vc,则它的nextResponder是 window,否则是父vc
  • UIWindow:它的nextResponder是UIApplication
  • UIApplication :它的nextResponder是app delegate。

所以整个事件在找到合适的view之后,判断当前view是否能处理这个事件,如果不能,则顺着nextResponder向父view传递,如果传递到VC也不能处理这个事件,则继续传递到UIWindow,如果window对象也不处理,则其将事件或消息传递给UIApplication对象,如果UIApplication也不能处理该事件或消息,则将其丢弃。其中任何一环能处理事件,则进行时间处理,整个事件的传递就结束了。

总结

事件处理的整个流程总结:

  1. 触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
  2. UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keyWindow)。
  3. key window会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
  4. 最合适的view会调用自己的touches方法处理事件
  5. touches默认做法是把事件顺着响应者链条向上抛,即顺着nextResponder向上传递。

事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

应用

1. 扩大按钮的响应区域

pointInside:withEvent:方法可以判断事件的点是否在当前view上,则可以重写该方法,扩大响应区域。有两种方式:

  1. 继承自 UIButton 重写该方法
  2. 使用分类,在分类中重写该方法。(不建议,因为分类会覆盖本类的实现)

方法1参考代码:
EnlargeButton.h:
“`objc
@interface EnlargeButton : UIButton

/// 扩大按钮 上|下|左|右 可点击区域
/// @param inset 上|下|左|右 扩大值

  • (void)setEnlargeEdge:(CGFloat)inset;

/// 扩大按钮 上|下|左|右 可点击区域
/// @param top 上边缘向外扩大值
/// @param left 左边缘向外扩大值
/// @param bottom 下边缘向外扩大值
/// @param right 右边缘向外扩大值

  • (void)setEnlargeEdgeWithTop:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right;

/// 返回当前按钮可以响应的热区大小

  • (CGRect)enlargedRect;

@end

import “EnlargeButton.h”

事件的生命周期大概为三个阶段:

  1. 事件的产生
  2. 事件的传递
  3. 事件的响应

事件的产生

苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()

当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。

事件的分类

iOS中的事件可以分为3大类型:

  • 触摸事件
  • 加速计事件
  • 远程控制事件

这里只关注触摸事件。

在iOS中,并不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,比如:

  • UIApplication
  • UIViewController
  • UIView

在UIResponder中提供了以下方法来处理触摸事件:

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

对于触摸事件,iOS中提供了UITouch对象对象,其作为用:

  • 保存着跟手指相关的信息,比如触摸的位置、时间、阶段
  • 当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
  • 当手指离开屏幕时,系统会销毁相应的UITouch对象

比如在UIView的几个touch方法中,就是将UITouch作为参数传递进去的:

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

UITouch的产生为:

当用户用一根手指触摸屏幕时,会创建一个与手指相关的UITouch对象,并且一根手指对应一个UITouch对象。如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象;如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象。

事件的传递

  • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中。
  • 然后UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
  • 主窗口keyWindow会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。

找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。

也就是说,触摸事件的传递是从父控件传递到子控件,即UIApplication->window->寻找处理事件最合适的view的一个过程。

寻找合适的控件来处理事件

  1. 首先判断主窗口(keyWindow)自己是否能接受触摸事件
  2. 判断触摸点是否在自己身上
  3. 子控件数组中从后往前遍历子控件,重复前面的两个步骤
  4. 找到view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
  5. 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件。

比如下面的四个View:白色、红色、蓝色、黄色,其中白色view是UIViewController的view,点击黄色view事件的传递过程为:

UIApplication -> UIWindow -> 白色view(UIViewController的view) -> 红色view -> 蓝色view -> 黄色view

iOS面试题目解析05- 事件传递和响应者链

找到合适的控件依赖于hitTest:withEvent:pointInside:withEvent:两个方法:

// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;  // default returns YES if point is in bounds - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   

hitTest:withEvent: 方法

  • 作用:寻找并返回最合适的view。
  • 调用时机:只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法。不管这个控件能不能处理事件,也不管触摸点在不在这个控件上,事件都会先传递给这个控件,随后再调用hitTest:withEvent:方法
- (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. 从后往前遍历子控件数组     for (int i = (int)self.subviews.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) { // 如果能找到最合适的view             return fitView;         }     }      // 4. 没有找到更合适的view,自己作为最合适处理这个事件的view。     return self; }

iOS面试题目解析05- 事件传递和响应者链

pointInside:withEvent: 方法

pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件。

事件的响应

事件从UIApplication->UIWindow->寻找处理事件最合适的view传递后,就会调用控件的touches方法来作具体的事件处touchesBegan…touchesMoved…touchedEnded…等。touches 方法就是对事件的响应,事件的响应是顺着响应链向上传递的,这个传递是依赖于UIResponder的nextResponder:

  • UIView :如果view是VC的root view,则它的nextResponder是 VC;否则是父view
  • UIViewController :如果 vc 是window的root vc,则它的nextResponder是 window,否则是父vc
  • UIWindow:它的nextResponder是UIApplication
  • UIApplication :它的nextResponder是app delegate。

所以整个事件在找到合适的view之后,判断当前view是否能处理这个事件,如果不能,则顺着nextResponder向父view传递,如果传递到VC也不能处理这个事件,则继续传递到UIWindow,如果window对象也不处理,则其将事件或消息传递给UIApplication对象,如果UIApplication也不能处理该事件或消息,则将其丢弃。其中任何一环能处理事件,则进行时间处理,整个事件的传递就结束了。

总结

事件处理的整个流程总结:

  1. 触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
  2. UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keyWindow)。
  3. key window会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
  4. 最合适的view会调用自己的touches方法处理事件
  5. touches默认做法是把事件顺着响应者链条向上抛,即顺着nextResponder向上传递。

事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

应用

1. 扩大按钮的响应区域

pointInside:withEvent:方法可以判断事件的点是否在当前view上,则可以重写该方法,扩大响应区域。有两种方式:

  1. 继承自 UIButton 重写该方法
  2. 使用分类,在分类中重写该方法。(不建议,因为分类会覆盖本类的实现)

方法1参考代码:
EnlargeButton.h:
“`objc
@interface EnlargeButton : UIButton

/// 扩大按钮 上|下|左|右 可点击区域
/// @param inset 上|下|左|右 扩大值

  • (void)setEnlargeEdge:(CGFloat)inset;

/// 扩大按钮 上|下|左|右 可点击区域
/// @param top 上边缘向外扩大值
/// @param left 左边缘向外扩大值
/// @param bottom 下边缘向外扩大值
/// @param right 右边缘向外扩大值

  • (void)setEnlargeEdgeWithTop:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right;

/// 返回当前按钮可以响应的热区大小

  • (CGRect)enlargedRect;

@end

import “EnlargeButton.h”

事件的生命周期大概为三个阶段:

  1. 事件的产生
  2. 事件的传递
  3. 事件的响应

事件的产生

苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()

当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。

事件的分类

iOS中的事件可以分为3大类型:

  • 触摸事件
  • 加速计事件
  • 远程控制事件

这里只关注触摸事件。

在iOS中,并不是任何对象都能处理事件,只有继承了UIResponder的对象才能接受并处理事件,比如:

  • UIApplication
  • UIViewController
  • UIView

在UIResponder中提供了以下方法来处理触摸事件:

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

对于触摸事件,iOS中提供了UITouch对象对象,其作为用:

  • 保存着跟手指相关的信息,比如触摸的位置、时间、阶段
  • 当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
  • 当手指离开屏幕时,系统会销毁相应的UITouch对象

比如在UIView的几个touch方法中,就是将UITouch作为参数传递进去的:

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

UITouch的产生为:

当用户用一根手指触摸屏幕时,会创建一个与手指相关的UITouch对象,并且一根手指对应一个UITouch对象。如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象;如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象。

事件的传递

  • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中。
  • 然后UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)。
  • 主窗口keyWindow会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步。

找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理。

也就是说,触摸事件的传递是从父控件传递到子控件,即UIApplication->window->寻找处理事件最合适的view的一个过程。

寻找合适的控件来处理事件

  1. 首先判断主窗口(keyWindow)自己是否能接受触摸事件
  2. 判断触摸点是否在自己身上
  3. 子控件数组中从后往前遍历子控件,重复前面的两个步骤
  4. 找到view,比如叫做fitView,那么会把这个事件交给这个fitView,再遍历这个fitView的子控件,直至没有更合适的view为止。
  5. 如果没有符合条件的子控件,那么就认为自己最合适处理这个事件。

比如下面的四个View:白色、红色、蓝色、黄色,其中白色view是UIViewController的view,点击黄色view事件的传递过程为:

UIApplication -> UIWindow -> 白色view(UIViewController的view) -> 红色view -> 蓝色view -> 黄色view

iOS面试题目解析05- 事件传递和响应者链

找到合适的控件依赖于hitTest:withEvent:pointInside:withEvent:两个方法:

// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;  // default returns YES if point is in bounds - (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   

hitTest:withEvent: 方法

  • 作用:寻找并返回最合适的view。
  • 调用时机:只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法。不管这个控件能不能处理事件,也不管触摸点在不在这个控件上,事件都会先传递给这个控件,随后再调用hitTest:withEvent:方法
- (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. 从后往前遍历子控件数组     for (int i = (int)self.subviews.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) { // 如果能找到最合适的view             return fitView;         }     }      // 4. 没有找到更合适的view,自己作为最合适处理这个事件的view。     return self; }

iOS面试题目解析05- 事件传递和响应者链

pointInside:withEvent: 方法

pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件。

事件的响应

事件从UIApplication->UIWindow->寻找处理事件最合适的view传递后,就会调用控件的touches方法来作具体的事件处touchesBegan…touchesMoved…touchedEnded…等。touches 方法就是对事件的响应,事件的响应是顺着响应链向上传递的,这个传递是依赖于UIResponder的nextResponder:

  • UIView :如果view是VC的root view,则它的nextResponder是 VC;否则是父view
  • UIViewController :如果 vc 是window的root vc,则它的nextResponder是 window,否则是父vc
  • UIWindow:它的nextResponder是UIApplication
  • UIApplication :它的nextResponder是app delegate。

所以整个事件在找到合适的view之后,判断当前view是否能处理这个事件,如果不能,则顺着nextResponder向父view传递,如果传递到VC也不能处理这个事件,则继续传递到UIWindow,如果window对象也不处理,则其将事件或消息传递给UIApplication对象,如果UIApplication也不能处理该事件或消息,则将其丢弃。其中任何一环能处理事件,则进行时间处理,整个事件的传递就结束了。

总结

事件处理的整个流程总结:

  1. 触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
  2. UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keyWindow)。
  3. key window会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
  4. 最合适的view会调用自己的touches方法处理事件
  5. touches默认做法是把事件顺着响应者链条向上抛,即顺着nextResponder向上传递。

事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

应用

1. 扩大按钮的响应区域

pointInside:withEvent:方法可以判断事件的点是否在当前view上,则可以重写该方法,扩大响应区域。有两种方式:

  1. 继承自 UIButton 重写该方法
  2. 使用分类,在分类中重写该方法。(不建议,因为分类会覆盖本类的实现)

方法1参考代码:
EnlargeButton.h:
“`objc
@interface EnlargeButton : UIButton

/// 扩大按钮 上|下|左|右 可点击区域
/// @param inset 上|下|左|右 扩大值

  • (void)setEnlargeEdge:(CGFloat)inset;

/// 扩大按钮 上|下|左|右 可点击区域
/// @param top 上边缘向外扩大值
/// @param left 左边缘向外扩大值
/// @param bottom 下边缘向外扩大值
/// @param right 右边缘向外扩大值

  • (void)setEnlargeEdgeWithTop:(CGFloat)top left:(CGFloat)left bottom:(CGFloat)bottom right:(CGFloat)right;

/// 返回当前按钮可以响应的热区大小

  • (CGRect)enlargedRect;

@end

import “EnlargeButton.h”

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » iOS面试题目解析05- 事件传递和响应者链求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们