You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:46:40