如何在UIView中制作透明孔洞及边缘添加透明圆形的最佳方案
在UIView中实现透明孔洞与边缘透明圆形的方案
嘿,这两个关于UIView透明孔洞的问题我刚好有不少实践经验,给你分享几个靠谱的实现方式,都是项目里常用的:
一、在UIView中制作透明孔洞
最灵活也最易维护的方法是利用CAShapeLayer结合路径来实现,核心思路是绘制一个包含孔洞的遮罩层,让View只显示遮罩以外的区域,或者直接绘制带孔洞的背景层。
方法1:使用CAShapeLayer作为遮罩
这是最常用的方案,适合需要保留View原有内容,只在特定区域挖洞的场景:
class HoleView: UIView { override func layoutSubviews() { super.layoutSubviews() let maskLayer = CAShapeLayer() maskLayer.frame = bounds // 1. 创建覆盖整个View的矩形路径 let rectPath = UIBezierPath(rect: bounds) // 2. 创建中心圆形孔洞的路径(可替换成任意形状) let holePath = UIBezierPath(ovalIn: CGRect(x: bounds.midX - 50, y: bounds.midY - 50, width: 100, height: 100)) // 3. 把孔洞路径添加到矩形路径中,开启奇偶填充规则 rectPath.append(holePath) rectPath.usesEvenOddFillRule = true maskLayer.path = rectPath.cgPath maskLayer.fillRule = .evenOdd // 4. 将遮罩应用到View上 self.layer.mask = maskLayer } }
这里的usesEvenOddFillRule是关键——它会让路径重叠的区域变成透明,完美实现“挖洞”效果,不管你要的是圆形、矩形还是不规则形状,只要调整holePath就行。
方法2:直接绘制带孔洞的背景层
如果你的View本身不需要背景色,而是要做一个带孔洞的半透明遮罩,直接创建CAShapeLayer作为子layer更合适:
class HoleOverlayView: UIView { override func layoutSubviews() { super.layoutSubviews() let backgroundLayer = CAShapeLayer() backgroundLayer.frame = bounds // 设置遮罩背景色(比如半透明黑) backgroundLayer.fillColor = UIColor.black.withAlphaComponent(0.7).cgColor let rectPath = UIBezierPath(rect: bounds) let holePath = UIBezierPath(ovalIn: CGRect(x: bounds.midX - 50, y: bounds.midY - 50, width: 100, height: 100)) rectPath.append(holePath) rectPath.usesEvenOddFillRule = true backgroundLayer.path = rectPath.cgPath backgroundLayer.fillRule = .evenOdd layer.addSublayer(backgroundLayer) } }
二、在UIView边缘创建透明圆形的最佳实现方式
既然你已经用Sketch完成了设计,我们可以精准还原边缘的透明圆形,思路和上面一致,只是把孔洞的位置放在View边缘即可。
场景1:顶部中间的边缘透明圆形
比如要做一个一半在View内、一半在View外的顶部圆形孔洞:
class EdgeHoleView: UIView { override func layoutSubviews() { super.layoutSubviews() let maskLayer = CAShapeLayer() maskLayer.frame = bounds let rectPath = UIBezierPath(rect: bounds) // 让圆形一半露出View外,实现边缘效果 let holeRect = CGRect(x: bounds.midX - 30, y: -30, width: 60, height: 60) let holePath = UIBezierPath(ovalIn: holeRect) rectPath.append(holePath) rectPath.usesEvenOddFillRule = true maskLayer.path = rectPath.cgPath maskLayer.fillRule = .evenOdd self.layer.mask = maskLayer } }
场景2:四个角的边缘透明圆形
如果是四个角各一个边缘圆形,只需要依次添加四个孔洞路径:
class CornerHoleView: UIView { override func layoutSubviews() { super.layoutSubviews() let maskLayer = CAShapeLayer() maskLayer.frame = bounds let rectPath = UIBezierPath(rect: bounds) let cornerRadius: CGFloat = 30 // 四个角的孔洞路径,每个都一半在View外 let topLeftHole = UIBezierPath(ovalIn: CGRect(x: -cornerRadius, y: -cornerRadius, width: cornerRadius*2, height: cornerRadius*2)) let topRightHole = UIBezierPath(ovalIn: CGRect(x: bounds.width - cornerRadius, y: -cornerRadius, width: cornerRadius*2, height: cornerRadius*2)) let bottomLeftHole = UIBezierPath(ovalIn: CGRect(x: -cornerRadius, y: bounds.height - cornerRadius, width: cornerRadius*2, height: cornerRadius*2)) let bottomRightHole = UIBezierPath(ovalIn: CGRect(x: bounds.width - cornerRadius, y: bounds.height - cornerRadius, width: cornerRadius*2, height: cornerRadius*2)) rectPath.append(topLeftHole) rectPath.append(topRightHole) rectPath.append(bottomLeftHole) rectPath.append(bottomRightHole) rectPath.usesEvenOddFillRule = true maskLayer.path = rectPath.cgPath maskLayer.fillRule = .evenOdd self.layer.mask = maskLayer } }
额外小提示
- 如果需要给孔洞加动画(比如位置移动、大小变化),直接给
CAShapeLayer的path属性加CABasicAnimation就行,比重新创建layer高效得多。 - 复杂形状的孔洞也可以用这个方法,只要用
UIBezierPath画出对应的路径即可。
内容的提问来源于stack exchange,提问作者Sagaya Abdul
相关产品推荐
相关产品推荐

