响应者链UIResponder-扩大UIButton的点击范围
在开发中,我们经常看到有按钮等的点击,会出现响应事件。按钮->view->ViewController->UIWindow->UIApplication,这就形成了一个响应链。本篇将讲述响应链的具体底层实现,大约花费10-15分钟左右,欢迎点评!!!
一、知识
继承UIResponder的对象,我们称之为响应者对象。我们常用到的UIApplication、UIWindow、UIViewController以及所有继承UIView的UIKit都会根继承于UIResponder。
UIResponder一般响应触摸事件,点按事件,加速事件以及远程控制事件:
触摸事件(touch handling)
- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches NS_AVAILABLE_IOS(9_1);
点按事件(press handling) NS_AVAILABLE_IOS(9_0)
- (void)pressesBegan:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesChanged:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesEnded:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesCancelled:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
加速事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
远程控制事件
- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);
响应者链:由多个响应者组合成的链条,我们就叫做响应者链,表示了响应者之间的联系。下面是响应者链关系图:
假设我们触摸了initial View,
1.第一响应者就是initial view 首先响应touchesBegan:withEvent:方法,然后传递给橘黄色的view
2.橘黄色的view开始去响应touchesBegan:withEvent:方法,然后传递给蓝绿色View
3.蓝绿色的view响应touchesBegan:withEvent:方法,然后传递给控制器的view
4.控制器view开始响应touchesBegan:withEvent:方法,然后传递给窗口
5.窗口再传递给application
假如上述响应者都不处理该事件,那么事件就会被丢弃。
事件的分发和传递
1.当程序发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中,
2.UIApplication将任务队列的最前端事件开始向下分发到UIWindow
3.UIWindow将事件向下分发到UIView
4.UIView首先看自己的是否处理事件,触摸点是否在自己的范围内,如果是,就开始继续找子视图
5.遍历子控件,重复上述上两步
6.如果没有找到,那么自己就是事件的处理者
7.如果自己不能处理,不做任何的处理。
>>>拓展
UIView不会接受事件处理的情况:
1)alpha < 0.01;
2) userInteractionEnabled = NO
3) hidden = YES
从父控件到子控件找处理事件最合适的view的期间,如果父视图不接受处理,则不需要向下走了,则子视图也不会接收到事件。在事件的处理中,关键在于是否有最合适的View来处理和响应事件,如果遍历了最后都还没有找到最合适的view来接受事件,则会被丢弃。
二、核心
寻找最合适的View
用到两个方法
// 此方法返回的View是本次点击事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event // 判断一个点是否落在范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
事件传递给窗口或者控件的话,就会调用hitTest:withEvent:方法寻找最合适的view,如果子控件满足是合适的view,则在子控件会再次调用hitTest:withEvent:来查看子控件到底是不是最合适的子view,一直这样递归,找到最合适的view,如果最终还没有找到,就会废弃事件。
思想可以用下面的代码表示:
// 因为所有的视图类都是继承BaseView
- (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.从后往前遍历自己的子控件
NSInteger count = self.subviews.count;
for (NSInteger i = count - ; i >= ; 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;
}
}
// 循环结束,表示没有比自己更合适的view
return self; }
判断触碰点是否在视图内
判断一个点是不是在视图内,通过下面方法判断
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
三、应用
在我们实际的开发过程中,我们也会经常使用这个方法,但并不是很多人知道使用这个方法。
- 使用这个方法增加按钮的点击范围:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
CGRect bounds = self.bounds;
bounds = CGRectInset(bounds, -, -);
// CGRectContainsPoint 判断点是否在矩形内
return CGRectContainsPoint(bounds, point);
}
- 不规则的按钮点击区域(自定义按钮的点击范围)
// // 改变图片的点击范围
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { // 控件范围宽度多40,高度20
CGRect bounds = CGRectInset(self.bounds, -, -);
NSLog(@"point = %@",NSStringFromCGPoint(point));
UIBezierPath *path1 = [UIBezierPath bezierPathWithRect:CGRectMake(-, , , )];
UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:CGRectMake(self.frame.size.width - , , , )];
if (([path1 containsPoint:point] || [path2 containsPoint:point])&& CGRectContainsPoint(bounds, point)){
//如果在path区域内,返回YES
return YES;
}
return NO;
}
上面的增加UIButton的点击范围
demo是以分类的方式的,利用runtime机制,核心代码如下:
我们来看看分类具体实现方式:
(1).UIButton+UIButtonExpand.h
#import <UIKit/UIKit.h>
#import <objc/runtime.h> @interface UIButton (UIButtonExpand)
- (void)expandButtonSize:(CGFloat)size; @end
(2).UIButton+UIButtonExpand.m
#import "UIButton+UIButtonExpand.h" static char expandSizeKey; @implementation UIButton (UIButtonExpand)
- (void)expandButtonSize:(CGFloat)size{
objc_setAssociatedObject(self, &expandSizeKey, [NSNumber numberWithFloat:size], OBJC_ASSOCIATION_COPY_NONATOMIC);
} - (CGRect)expandRect{
NSNumber *expandSize = objc_getAssociatedObject(self, &expandSizeKey);
if (expandSize) {
return CGRectMake(self.bounds.origin.x - expandSize.floatValue,
self.bounds.origin.y - expandSize.floatValue,
self.bounds.size.width + expandSize.floatValue + expandSize.floatValue,
self.bounds.size.height + expandSize.floatValue + expandSize.floatValue);
} else {
return self.bounds;
}
} - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { CGRect buttonRect = [self expandRect];
if (CGRectEqualToRect(buttonRect, self.bounds)) {
return [super pointInside:point withEvent:event];
}
return CGRectContainsPoint(buttonRect, point) ? YES : NO;
} @end
下面是demo的地址:https://github.com/zxy1829760/UIResponderDemo
以上就是UIResponder和响应链的基本内容,欢迎大家指正。
响应者链UIResponder-扩大UIButton的点击范围的更多相关文章
- [功能]点击ImageView进入页面,时间响应者链实现
app点击一个按钮跳转到另外一个控制器非常常用,但是如果是点击的是UIImageView如何通过模态视图进入另外一个控制器呢?万一这个UIImageView在自定义的cell或者view里面,那该如何 ...
- Responder一点也不神秘————iOS用户响应者链完全剖析
一.事件分类 对于IOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕.晃动设备.通过遥控设施控制设备.对应的事件类型有以下三种: 1.触屏事件(Touch Event) 2.运动事件(Moti ...
- iOS Responder Chain 响应者链
一.事件分类 对于IOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕.晃动设备.通过遥控设施控制设备.对应的事件类型有以下三种: 1.触屏事件(Touch Event) 2.运动事件(Moti ...
- IOS开发中响应者链
在IOS开发中,有时候会遇到如下情况:在页面1上有一个RedView,在RedView上有一个GreenView,在GreenView上有一个button,这些view的创建代码如下: 1.AppDe ...
- 理解cocoa和cocoa touch的响应者链
该文章翻译自:Understanding cocoa and cocoa touch responder chain. 转载注明出处:http://www.cnblogs.com/zhanggui/p ...
- iOS 事件响应者链的学习(也有叫 UI连锁链)
当发生事件响应的时候,必须知道由谁来响应事件.在iOS中,由响应链来对事件进行响应,所有的事件响应的类都是继承于UIResponder的子类,响应链是一个由不同对象组成的层次结构,其中每个对象将依次获 ...
- Cocoa Touch事件处理流程--响应者链
Cocoa Touch事件处理流程--响应者链 作者:wangzz 原文地址:http://blog.csdn.net/wzzvictory/article/details/9264335 转载请注明 ...
- [置顶] Responder一点也不神秘————iOS用户响应者链完全剖析
这篇文章想跟大家分享的主旨是iOS捕获用户事件的各种情况,以及内部封装的一些特殊事件. 我们先从UIButton谈起,UIButton大家使用的太多了,他特殊的地方就在于其内置的普通Default/高 ...
- 从uibutton的点击谈谈ios的响应事件
最近在做一个项目,接连遇到两个关于点击事件的问题. 1.点击button不能响应事件的. 2.子view的frame超出了父view的容器大小,也不能响应点击事件. 效果图如右: 1.第一张图中的弹出 ...
随机推荐
- vs 2017 打开 iis express问题
问题: 更新vs2017 15.6.4后,首次打开网站 iis express 一直报 无法连接到web服务器. 解决办法: 关闭防火墙,在次启动即可,启动成功后,在次打开防火墙也无影响.
- SSM_CRUD新手练习(10)返回分页的JSON数据
我们完成了员工的分页查询,但是现在这种做法只能适应浏览器和服务器的交互模式,但在移动互联网时代,客户端不仅仅只有浏览器,还有安卓和IOS客户端.我们的解决方式是AJAX+JSON方式来实现平台无关性. ...
- Python小练习之寻找101到200之间的素数
方法1:from math import * def primeNumber(start,end): num = 0 for i in range(start,end): flag = 0 for j ...
- 【分布式缓存系列】集群环境下Redis分布式锁的正确姿势
一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于 ...
- Converting Recursive Traversal to Iterator
In this article, I'm going to introduce a general pattern named Lazy Iterator for converting recursi ...
- Java中线程同步锁和互斥锁有啥区别?看完你还是一脸懵逼?
首先不要钻概念牛角尖,这样没意义. 也许java语法层面包装成了sycnchronized或者明确的XXXLock,但是底层都是一样的.无非就是哪种写起来方便而已. 锁就是锁而已,避免多个线程对同一个 ...
- Metasploit Framework(8)后渗透测试(一)
文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 使用场景: Kali机器IP:192.168.163. ...
- Kali学习笔记20:缓冲区溢出实验环境准备
在前几篇的博客中:我介绍了OpenVAS和Nessus这两个强大的自动化漏洞扫描器 但是,在计算机领域中有种叫做0day漏洞:没有公开只掌握在某些人手中 那么,这些0day漏洞是如何被发现的呢? 接下 ...
- springbean的生命周期
1.Spring对Bean进行实例化(相当于程序中的new Xx())2.Spring将值和Bean的引用注入进Bean对应的属性中3.如果Bean实现了BeanNameAware接口,Spring将 ...
- ubuntu16.04 uninstall cuda 9.0 completely and install 8.0 instead
卸载cuda 9.0sudo apt-get --purge remove cudasudo apt autoremoveto remove cuda 9.0 Thensudo apt-get cle ...