如何通过代码为带圆形子视图的UIView创建青蓝色遮罩?
实现青蓝色遮罩的两种实用方案
嘿,我来帮你搞定这个青蓝色遮罩的需求!根据你的描述,你需要在带红色边框的主UIView上,给非圆形区域加上青蓝色遮罩,只透出渐变圆形的部分对吧?下面给你两种直接能用的实现方法~
方案一:用CAShapeLayer做遮罩层(最灵活)
核心思路是:先创建一个铺满主视图的青蓝色视图,然后给它加一个「挖空圆形区域」的遮罩,这样青蓝色就只会显示在圆形以外的地方。
Swift 代码示例
// 假设你的主视图叫 containerView,circleViews 是包含5个圆形子视图的数组 // 1. 创建青蓝色遮罩视图 let maskOverlay = UIView(frame: containerView.bounds) maskOverlay.backgroundColor = UIColor(red: 0/255, green: 180/255, blue: 216/255, alpha: 0.7) // 可调整颜色和透明度 maskOverlay.tag = 100 // 方便后续更新时快速定位 containerView.addSubview(maskOverlay) maskOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight] // 适配主视图大小变化 // 2. 创建遮罩路径(实现挖空圆形的效果) let maskLayer = CAShapeLayer() maskLayer.frame = maskOverlay.bounds maskLayer.fillRule = .evenOdd // 关键规则:偶数次覆盖的区域会透明,奇数次保留 // 先添加整个主视图的矩形路径 let fullRectPath = UIBezierPath(rect: maskOverlay.bounds) maskLayer.path = fullRectPath.cgPath // 逐个添加圆形路径(用来挖空) for circleView in circleViews { // 把圆形的坐标转换到遮罩视图的坐标系 let circleFrameInOverlay = containerView.convert(circleView.frame, to: maskOverlay) let circlePath = UIBezierPath(ovalIn: circleFrameInOverlay) maskLayer.path = maskLayer.path?.copy(byAppending: circlePath.cgPath) } // 3. 给遮罩视图应用遮罩 maskOverlay.layer.mask = maskLayer
Objective-C 代码示例
// 假设 containerView 和 circleViews 已初始化完成 // 1. 创建青蓝色遮罩视图 UIView *maskOverlay = [[UIView alloc] initWithFrame:containerView.bounds]; maskOverlay.backgroundColor = [UIColor colorWithRed:0/255.0 green:180/255.0 blue:216/255.0 alpha:0.7]; maskOverlay.tag = 100; [containerView addSubview:maskOverlay]; maskOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; // 2. 创建遮罩路径 CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.frame = maskOverlay.bounds; maskLayer.fillRule = kCAFillRuleEvenOdd; UIBezierPath *fullRectPath = [UIBezierPath bezierPathWithRect:maskOverlay.bounds]; maskLayer.path = fullRectPath.CGPath; for (UIView *circleView in circleViews) { CGRect circleFrameInOverlay = [containerView convertRect:circleView.frame toView:maskOverlay]; UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:circleFrameInOverlay]; maskLayer.path = CGPathCreateCopyByAppendingPath(maskLayer.path, circlePath.CGPath); } // 3. 应用遮罩 maskOverlay.layer.mask = maskLayer;
方案二:适配AutoLayout的动态更新
如果你的主视图会随着屏幕旋转、布局变化而改变大小,记得在layoutSubviews里更新遮罩路径,保证遮罩始终贴合主视图:
Swift 示例(主视图子类中)
class CustomContainerView: UIView { var circleViews: [UIView] = [] // 存储你的圆形子视图 override func layoutSubviews() { super.layoutSubviews() // 找到之前添加的遮罩视图 guard let maskOverlay = subviews.first(where: { $0.tag == 100 }), let maskLayer = maskOverlay.layer.mask as? CAShapeLayer else { return } // 更新遮罩层的frame和路径 maskLayer.frame = maskOverlay.bounds let fullRectPath = UIBezierPath(rect: maskOverlay.bounds) maskLayer.path = fullRectPath.cgPath for circleView in circleViews { let circleFrame = convert(circleView.frame, to: maskOverlay) let circlePath = UIBezierPath(ovalIn: circleFrame) maskLayer.path = maskLayer.path?.copy(byAppending: circlePath.cgPath) } } }
关键细节说明
fillRule = .evenOdd是核心:我们先画了整个矩形(填充次数1),每个圆形又叠加一次(填充次数2),所以圆形区域因为是偶数次填充会变成透明,刚好透出下面的渐变圆形。- 颜色和透明度可以根据需求调整,修改
alpha值就能控制遮罩的深浅。 - 如果需要反过来(青蓝色覆盖圆形,其他区域透明),只需要把
fillRule改成.nonZero就行。
内容的提问来源于stack exchange,提问作者TheBearF8
相关产品推荐
相关产品推荐

