时间:2021-05-20
这是一种实现 UIView 镂空效果的方案,可以快速实现任意形状的镂空、文字的镂空、带镂空的毛玻璃效果等。本质上是 UIView 的 maskView 效果。
前言
首先来复习一下遮罩效果的实现。如果我们有一张图片,又恰好有一个圆,当我们把圆设置为图片的遮罩时,会得到这样的结果。
代码实现看上去像是这样:
view.maskView = maskView;那么问题来了,如果我们希望得到下面的结果,该怎么做呢?这看起来像是图层的相减,即原来的图层减去遮罩的部分。
可惜苹果爸爸不够贴心,没有提供方便的接口调用。让我们来看看可以怎么实现。
一、思路
我们的最终目标是,封装出一个接口,调用方式类似于 maskView 属性,可以很方便地对一个 UIView 做镂空效果。
注:以下用 originView 指代需要上效果的 view ,用 maskView 指代充当遮罩的 view 。
目前看来,可以从两个方向入手:
方式一是指,在设置这个属性的时候,对 originView 的视图进行重新绘制,然后在绘制的时候,减掉 maskView 的区域。
方式二是指,当拿到 maskView 的时候,先对 maskView 本身先进行处理,将遮罩范围取反。然后再做遮罩效果,由于遮罩的区域已经相反,于是得到的结果也是相反的,就达到镂空的目的。
看上去方式二比较靠谱,而且最后是调用 UIView 的 setMaskView: 来实现,还可以保留原来遮罩的一些特性。比如当修改 maskView 的 frame 的时候, originView 的遮罩位置也会相应改变。
二、实现
生成相反的遮罩图可以分为三步。假设一开始拿到的 maskView 是下面这样,让我们来看下,转换过程中遮罩图每一步的变化。
注:为了更直观的效果,图片中透明的部分用灰白相间格子来表示(以下相同)。
1、将 maskView 转化为 UIImage
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);CGContextTranslateCTM(UIGraphicsGetCurrentContext(), view.frame.origin.x, view.frame.origin.y);[view.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();这一步拿到了 maskView 对应的 image 图像。此时遮罩图的大小会被同步为 originView 的大小。
2、将
UIImage 转换为只有 alpha 通道的 CGContextRef
CGImageRef originalMaskImage = [image CGImage];float width = CGImageGetWidth(originalMaskImage);float height = CGImageGetHeight(originalMaskImage); int strideLength = ROUND_UP(width * 1, 4);unsigned char * alphaData = calloc(strideLength * height, sizeof(unsigned char));CGContextRef alphaOnlyContext = CGBitmapContextCreate(alphaData, width, height, 8, strideLength, NULL, kCGImageAlphaOnly); CGContextDrawImage(alphaOnlyContext, CGRectMake(0, 0, width, height), originalMaskImage);这时候的 alphaOnlyContext 对应的图像是下面这样,只保留了 alpha 通道。
3、将
CGContextRef 中的 alpha 值进行遍历转换
for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { unsigned char val = alphaData[y*strideLength + x]; val = 255 - val; alphaData[y*strideLength + x] = val; }} CGImageRef alphaMaskImage = CGBitmapContextCreateImage(alphaOnlyContext);UIImage *result = [UIImage imageWithCGImage:alphaMaskImage];转换后,获得的 result 图像是:
于是,我们就可以用 result 愉快地进行 mask 了。
三、使用
我们可以将上述的步骤,封装为一个方法,用 category 来实现。
@interface UIView (MFSubtractMask)- (void)setSubtractMaskView:(UIView *)view;- (UIView *)subtractMaskView;@end这样调用起来就十分方便了,一行代码搞定:
view.subtractMaskView = maskView;四、局限性
1. subtractMaskView 不会自动刷新
我们知道,当 UIView 的 maskView 的内容动态修改时,会实时反映到 UIView 中。但在本项目中, subtractMaskView 属性会生成一张全新的图片来作为遮罩图,因为不会根据 subtractMaskView 的内容实时来刷新视图。如果需要更新,必须手动调用 setSubtractMaskView: 方法来重新生成遮罩图。
2. setSubtractMaskView: 不宜被频繁调用
setSubtractMaskView: 本质上是生成一个新的遮罩图的过程,该过程涉及图片像素的遍历转换,较为耗时,不宜频繁调用。
综上所述,这种方案适合只生成一次遮罩图的场景。
五、源码
请到 GitHub 上查看完整代码。
总结
以上所述是小编给大家介绍的iOS中一行代码实现 UIView 镂空效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
vue+elementUi实现密码显示/隐藏+小图标变化(js一共三行代码,其中一行为了美观)...,先给大家展示下效果图,感觉不错可以参考实现代码。【效果图】
ios中一般会用到几种方式自定义uiview1、继承之uiview的存代码的自定义view2、使用xib和代码一起使用的自定义view3、存xib的自定义vie
无边框窗口的实现只需要一行代码即可实现this->setWindowFlags(Qt::FramelessWindowHint);代码及运行效果:无边框窗口能拖
本文实例讲述了jQuery实现行文字链接提示效果的方法。分享给大家供大家参考。具体实现方法如下:复制代码代码如下:一行文字的提示效果$(function(){$
效果:竖排列表元素上下滚动上一行显示下一行消失代码:标题内容jQuery(document).ready(function(e){vartimer=setInt