【原】ios的hitTest方法以及不规则区域内触摸事件处理方法

在正常的使用场景中,我们处理了比较多的矩形区域内触摸事件,比如UIButton、UIControl。一般来说,这些控件的图形以及触摸区域都是矩形或者圆角矩形的。但是在一些特殊应用场景中我们有时不得不面对这样一种比较严苛的需求,比如要求程序只对某个圆形、五角形等非常规区域的点击事件进行处理,这就需要花点功夫了。本文以圆形为例子来介绍此类场景的处理方法。

先看下面一张图(附图1),我们的目标是实现如下自定义tabbar。中间带突起圆形的自定义tabbar曾一度流行,今天我们来粗糙地实现一下。

在附图一中,红色代表tabbar,上面有三个蓝色按钮。在三个按钮中我们重点解决按钮A,因为它有一半的区域突在tabbar的有效区域外。

对于按钮A,我们有以下两个问题需要解决:

1、如何准确过滤掉A外接矩形里非蓝色区域的点击事件?

2、如何让A的上半部分也能响应触摸事件?

其实两个问题的解决方法是基本一致的。在iOS中所有控件都是以矩形的方式存在的,在图2中尽管蓝色部分看起来是圆形,但当点击外接矩形内的非圆形区域时也会默认触发点击事件。因此,我们需要用一些手段把触摸事件“拦截”下来。想要“拦截”事件,就必须了解iOS的事件分发机制,也就是当你点击设备屏幕后,iOS是如何决定由那个view去最终响应你的触摸!下面插播一小段关于iOS事件分发的介绍:

==================================

当你手指触摸屏幕后会发生以下事情:触摸事件被封装成一个UIEvent事件,去当前iOS操作系统的active app队列中取当前活跃的APP,把event传给它--->event传给UIApplication--->传给UIWindow的root view controller(rootVC)--->调用rootVC.view的所有subviews的hitTest:event:方法。哪个view的hitTest:event方法返回非nil值,则触摸事件就交给该view处理。关于事件分发的详细机制及举例可以参考技术哥大神的文章

==================================

让我们重新回到探讨的问题上。通过以上简介我们可以知道,想“拦截”触摸事件,则应该在tabbar的hitTest:event方法中做处理(坐标判断等)。以下是具体的demo源码:

1 #import <UIKit/UIKit.h>
2
3 @interface panelView : UIView
4
5 @end

 1 #import "panelView.h"
 2
 3 @implementation panelView
 4
 5 - (id)initWithFrame:(CGRect)frame
 6 {
 7     self = [super initWithFrame:frame];
 8     if (self) {
 9         [self initSubviews];
10     }
11     return self;
12 }
13
14 - (void)initSubviews
15 {
16     UIButton *roundBtn = [UIButton buttonWithType:UIButtonTypeCustom];
17     roundBtn.frame = CGRectMake(self.frame.size.width / 2 - 30, -30, 60, 60);
18     roundBtn.backgroundColor = [UIColor blueColor];
19     roundBtn.layer.cornerRadius = 30;
20     roundBtn.tag = 10086;
21     [roundBtn addTarget:self action:@selector(onBtnPressed:)
22        forControlEvents:UIControlEventTouchUpInside];
23     [self addSubview:roundBtn];
24
25     UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeCustom];
26     leftBtn.frame = CGRectMake(0, 15, 30, 30);
27     leftBtn.backgroundColor = [UIColor blueColor];
28     leftBtn.tag = 10087;
29     [leftBtn addTarget:self action:@selector(onBtnPressed:)
30        forControlEvents:UIControlEventTouchUpInside];
31     [self addSubview:leftBtn];
32
33     UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
34     rightBtn.frame = CGRectMake(self.frame.size.width - 30, 15, 30, 30);
35     rightBtn.backgroundColor = [UIColor blueColor];
36     rightBtn.tag = 10088;
37     [rightBtn addTarget:self action:@selector(onBtnPressed:)
38        forControlEvents:UIControlEventTouchUpInside];
39     [self addSubview:rightBtn];
40 }
41
42 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
43 {
44     UIView *hitView = nil;
45     //NSLog(@"point:%@", NSStringFromCGPoint(point));
46     UIButton *roundBtn = (UIButton *)[self viewWithTag:10086];
47     UIButton *leftBtn = (UIButton *)[self viewWithTag:10087];
48     UIButton *rightBtn = (UIButton *)[self viewWithTag:10088];
49     BOOL pointInRound = [self touchPointInsideCircle:roundBtn.center radius:30 targetPoint:point];
50     if (pointInRound) {
51         hitView = roundBtn;
52     } else if(CGRectContainsPoint(leftBtn.frame, point)) {
53         hitView  = leftBtn;
54     } else if(CGRectContainsPoint(rightBtn.frame, point)) {
55         hitView = rightBtn;
56     } else {
57         hitView = self;
58     }
59     return hitView;
60 }
61
62 - (BOOL)touchPointInsideCircle:(CGPoint)center radius:(CGFloat)radius targetPoint:(CGPoint)point
63 {
64     CGFloat dist = sqrtf((point.x - center.x) * (point.x - center.x) +
65                          (point.y - center.y) * (point.y - center.y));
66     return (dist <= radius);
67 }
68
69
70 - (void)onBtnPressed:(id)sender
71 {
72     UIButton *btn = (UIButton *)sender;
73     NSLog(@"btn tag:%d", btn.tag);
74 }
75
76 @end

panelView.m(控件初始化,不展开)

在hitTest方法中最重要的是判断按钮A所在的区域,其实仅仅用到两点的距离公式来圈出蓝色部分所在的圆形,判断方法如下:

1 - (BOOL)touchPointInsideCircle:(CGPoint)center radius:(CGFloat)radius targetPoint:(CGPoint)point
2 {
3     CGFloat dist = sqrtf((point.x - center.x) * (point.x - center.x) +
4                          (point.y - center.y) * (point.y - center.y));
5     return (dist <= radius);
6 }

而判断点是否在按钮B/C内就更简单了,系统提供了封装好的api:

1 bool CGRectContainsPoint(CGRect rect, CGPoint point)

最终,关于事件“拦截”的判断如下:

 1 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
 2 {
 3     UIView *hitView = nil;
 4     //NSLog(@"point:%@", NSStringFromCGPoint(point));
 5     UIButton *roundBtn = (UIButton *)[self viewWithTag:10086];
 6     UIButton *leftBtn = (UIButton *)[self viewWithTag:10087];
 7     UIButton *rightBtn = (UIButton *)[self viewWithTag:10088];
 8     BOOL pointInRound = [self touchPointInsideCircle:roundBtn.center radius:30 targetPoint:point];
 9     if (pointInRound) {
10         hitView = roundBtn;
11     } else if(CGRectContainsPoint(leftBtn.frame, point)) {
12         hitView  = leftBtn;
13     } else if(CGRectContainsPoint(rightBtn.frame, point)) {
14         hitView = rightBtn;
15     } else {
16         hitView = self;
17     }
18     return hitView;
19 }

此外,在hitTest中还可以玩其他花样,比如将本该由按钮A响应的时间强制性转发给其他按钮,这只需在hitTest的返回值中修改一下即可!

本文所用demo完整源码点这里test.zip下载。

   

图1                   图2

时间: 2024-07-28 20:01:06

【原】ios的hitTest方法以及不规则区域内触摸事件处理方法的相关文章

ios的hitTest方法以及不规则区域内触摸事件处理方法

ios的hitTest方法以及不规则区域内触摸事件处理方法 概述 在正常的使用场景中,我们处理了比较多的矩形区域内触摸事件,比如UIButton.UIControl.一般来说,这些控件的图形以及触摸区域都是矩形或者圆角矩形的.但是在一些特殊应用场景中我们有时不得不面对这样一种比较严苛的需求,比如要求程序只对某个圆形.五角形等非常规区域的点击事件进行处理,这就需要花点功夫了.本文以圆形为例子来介绍此类场景的处理方法. 先看下面一张图(附图1),我们的目标是实现如下自定义tabbar.中间带突起圆形

SAP ABAP编程 用LOOP READ TABLE的方法代替双LOOP内表的方法

如上,用双LOOP的方法实现两个内表匹配筛选性能不高,可以换成下面LOOP READ TABLE的方法 注意问题,gwa_vbap工作区在READ TABLE之前需要清空.

IOS开发——UI进阶篇(十二)事件处理,触摸事件,UITouch,UIEvent,响应者链条,手势识别

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

猫猫学IOS(二十五)UI之触摸事件

猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents iOS中的事件 在用户使用app过程中,会产生各种各样的事件;iOS中的事件可以分为3大类型: 响应者对象–UIResponder 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为"响应者对象" UIApplication.UIViewController.UI

Android EventBus的简单使用基本的使用步骤就是如下4步,点击此链接查看例子及介绍。 定义事件类型: `public class MyEvent {}` 定义事件处理方法: `public

基本的使用步骤就是如下4步,点击此链接查看例子及介绍. 定义事件类型: `public class MyEvent {}` 定义事件处理方法: `public void onEventMainThread` 注册订阅者: `EventBus.getDefault().register(this)` 发送事件: `EventBus.getDefault().post(new MyEvent())` 一.实现 **EventBus**使用方法很简单,但用一个东西,如果不了解它的实现用起来心里总是没底

解决OSPF不规则区域几个方法

因为刚学OSPF路由协议,下面只是个人实验结果,可能也有不准确的地方,请批评指正,谢谢 解决OSPF不规则区域 1.多进程双向重分布 2.Tunnel隧道 3.virtual-link 1.多进程双向重分布: 拓扑: 在R3上有两个OSPF进程,当R3双向多进程重分布时,可以看到两边的路由都能学到 关键配置: [R1]dis cur # sysname R1 # router id 91.1.1.1 # interface Serial0/0/0 link-protocol ppp ip add

利用颜色和形态学两种方法进行车牌区域提取的OpenCV代码

要想提取车牌号,首先你要定位车牌区域嘛,本文分别两种方法用,即颜色和形态学的方法,对车牌区域进行判定.说得是两种方法,其实两种方法并无多大的区别,只是有一步的判断标准不一样而已,你看了下面整理出的的思路就知道两者的区别真的很小了. 方法一:利用颜色提取车牌区域的思路: ①求得原图像的sobel边缘sobelMat ②在HSV空间内利用车牌颜色阈值对图像进行二值化处理,得到图像bw_blue→ ③由下面的判别标准得到图像bw_blue_edge for (int k = 1; k != heigh

原 IOS之NSValue整理

原 IOS之NSValue整理 发表于2年前(2013-02-28 23:02)   阅读(1974) | 评论(0) 5人收藏此文章, 我要收藏 赞3 IOS NSValue 值对象(value object)概念 在面向对象的编程语言中,值对象本质上是数据元素的的对象包装器,所谓数据元素,常见的包含string,number,date类型以及其它自定义的结构体类型.Objective-C语言本身提供了string,number,date相对应的包装类,分别是NSString,NSNumber

原 iOS面试题收集

原 iOS面试题收集 发表于2年前(2013-07-22 13:47)   阅读(369) | 评论(0) 4人收藏此文章, 我要收藏 赞0 听云性能监测产品App.Server.CDN免费试用,绑定账号送京东卡 iOS 面试题 Objective-C 1.ARC 是什么? ARC 是 iOS 5 推出的新功能,全称叫 ARC(Automatic Reference Counting).简单 地说,就是代码中自动加入了 retain/release,原先需要手动添加的用来处理内存管 理的引用计数