热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS实现UIImageView透明区域点击事件穿透

原文转自:http:wonderffee.github.ioblog20130710pass-touch-event-through-uiimageviews-tra

原文转自:http://wonderffee.github.io/blog/2013/07/10/pass-touch-event-through-uiimageviews-transparent-area-in-ios/


问题

最近要在iPad上实现一个很独特的功能,简单描述一下就是要显示一个带有半透明背景的弹出界面,在其上加一个不规则形状的图片,手指点击这个弹出界面的半透明区域就退出这个弹出界面。

问题是UED/美工不会提供纯粹的不规则形状切图,实际只能给出的是以不规则形状加透明区域的矩形切图,这就带来另外一个要求:点击矩形切图的透明区域也要退出弹出界面。这就有点难办了,透明区域也是不规则形状的,该怎么判断出手指点击的点就是透明区域呢?

思路

一般在iOS的控件中,要不就是完全允许用户点击,要不就是禁止用户交互,这是可以通过设置控件的userInteractionEnabled属性来修改。如果添加的图片不是不规则形状的,而是矩形,这问题就简单多了,只需要将矩形图片对应的UIImageView的userInteractionEnabled设为YES,对半透明背景View(或者直接设置为一个按钮)设置点击事件处理,就可以点击实现半透明背景退出弹出界面。

现在的情况是这个矩形图片一分为二,一部分为实体的不规则形状图片,一部分为不规则形状的透明区域。很显然,问题的解决思路是:让手指能“穿透”这个不规则透明区域去点击背后的半透明背景,而不透明部分就不“穿透”。

前面说的userInteractionEnabled属性只是简单地一刀切设置控件是否允许用户操作(即可以响应手指触摸事件),更加灵活的设置方法是使用UIView的hitTest:withEvent:与pointInside:withEvent:。简单介绍下,iOS中的pointInside:withEvent:方法是用来判断当前的点击或者触摸事件的点是否在当前的view中,它被hitTest:withEvent:调用,通过对每个子视图调用pointInside:withEvent:决定最终哪个视图来响应此事件。如果一个子视图的pointInside:withEvent:返回NO,说明这个子视图不会响应点击事件,然后就去寻找更深层的子视图来找到最终响应触摸事件;返回YES就说明子视图能响应点击事件(但不一定是子视图本身响应,若子视图还有子视图的话,还会继续循环去找最终响应事件的子子视图)。

于是,本文的问题就可以这样转化:创建一个UIImageView的子类,重写pointInside:withEvent:方法,让矩形图片的透明区域的pointInside:withEvent:返回NO,而非透明区域的pointInside:withEvent:返回YES,如果能达到这个要求,透明区域点击事件穿透就能够实现。

现在的关键问题是怎么识别出这个透明区域。 
iOS中通常用的图片是PNG图片,这种图片有alpha通道,如果能获取PNG图片每个像素的alpha值,就不难判断出手指点击的图片区域是不是透明的。

关键代码如下:

Here’s Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
//Using code from http://stackoverflow.com/questions/1042830/retrieving-a-pixel-alpha-value-for-a-uiimage

unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel,
1, 1, 8, 1, NULL,
kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[self.image drawAtPoint:CGPointMake(-point.x, -point.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0f;
BOOL transparent &#61; alpha < 0.01f;

return !transparent;
}

解释&#xff1a; 
这段代码是通过CGBitmapContextCreate方法创建只包含alpha通道的图形上下文&#xff08;真不知道context怎么翻译为最好&#xff09;&#xff0c;这个图形上下文的大小为1x1&#xff0c;也就是实际上只放得下一个像素&#xff0c;将矩形图片手指触摸点point绘制到这个图形上下文中&#xff0c;那么pixel数组中唯一元素的值就是手指触摸点那一个像素的alpha值&#xff0c;做归一化为与0.01比较&#xff0c;如果小于0.01就表明手指触摸点是透明的&#xff0c;这时候返回NO就能够实现穿透效果&#xff0c;相反大于0.01就不会穿透。

注意到代码中用到的坐标为(-point.x, -point.y)&#xff0c;为什么会是负数呢&#xff1f;这是因为如果context的区域大小与image一致的话&#xff0c;[image drawAtPoint:]就会将image全部绘制在context中&#xff0c;而实际上context只放得下一个像素&#xff0c;为了保证point点能刚好绘制在这个context上&#xff0c;就必须设置绘制的起始坐标为(-point.x, -point.y)。

代码中的UIGraphicsPushContext容易误导人&#xff0c;看名字以为是将参数中指定的context push入栈&#xff0c;但是参数中的context明明就是刚创建的啊&#xff1f;其实它是将旧的context&#xff08;默认的context&#xff09;入栈&#xff0c;再切换到新的context&#xff08;也就是参数中指定的&#xff09;绘制&#xff0c;执行UIGraphicsPopContext后就会切换回旧的context&#xff0c;而在新的context上绘制的内容完全不影响旧context&#xff08;默认context&#xff09;。这与CGContextSaveGState和CGContextRestoreGState是有本质区别的。

附CGBitmapContextCreate函数参数详解: 
原型&#xff1a;

1
2
3
4
5
6
7
8
9

CGContextRef CGBitmapContextCreate (
void *data,
size_t width,
size_t height,
size_t bitsPerComponent,
size_t bytesPerRow,
CGColorSpaceRef colorspace,
CGBitmapInfo bitmapInfo
);

参数&#xff1a; 
data 指向要渲染的绘制内存的地址。这个内存块的大小至少是&#xff08;bytesPerRow*height&#xff09;个字节 
width bitmap的宽度,单位为像素 
height bitmap的高度,单位为像素 
bitsPerComponent 内存中像素的每个组件的位数.例如&#xff0c;对于32位像素格式和RGB 颜色空间&#xff0c;你应该将这个值设为8. 
bytesPerRow bitmap的每一行在内存所占的比特数 
colorspace bitmap上下文使用的颜色空间。 
bitmapInfo 指定bitmap是否包含alpha通道&#xff0c;像素中alpha通道的相对位置&#xff0c;像素组件是整形还是浮点型等信息的字符串。

描述&#xff1a; 
当你调用这个函数的时候&#xff0c;Quartz创建一个位图绘制环境&#xff0c;也就是位图上下文。当你向上下文中绘制信息时&#xff0c;Quartz把你要绘制的信息作为位图数据绘制到指定的内存块。一个新的位图上下文的像素格式由三个参数决定&#xff1a;每个组件的位数&#xff0c;颜色空间&#xff0c;alpha选项。alpha值决定了绘制像素的透明性。

参考资料&#xff1a;

  • CGBitmapContextCreate函数参数详解
  • UiView事件传递相关函数&#xff1a;pointInside:withEvent:
  • iOS中管理图形上下文
  • Detect touches only on non-transparent pixels of UIImageView, efficiently

 Jul 10th, 2013

原创文章&#xff0c;版权声明&#xff1a;自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0




推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 本文介绍了如何使用MATLAB调用摄像头进行人脸检测和识别。首先需要安装扩展工具,并下载安装OS Generic Video Interface。然后使用MATLAB的机器视觉工具箱中的VJ算法进行人脸检测,可以直接调用CascadeObjectDetector函数进行检测。同时还介绍了如何调用摄像头进行人脸识别,并对每一帧图像进行识别。最后,给出了一些相关的参考资料和实例。 ... [详细]
  • 目前Miniconda3的主要版本已经不支持python3.6,以Windows为例,在官网Miniconda—Condadocumentation中只有python3.7 ... [详细]
author-avatar
davidwzw2009413
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有