SwiftUI如何使用inverted mask实现半透明遮罩镂空高亮效果
问题原因
- 你的
HoleShapeMask仅定义了需要镂空的高亮区域路径,没有先绘制全屏尺寸的底层路径,导致eoFill奇偶填充规则无法生效(该规则需要至少两层重叠路径才能计算镂空区域) - 遮罩层的frame布局优先级设置不当,没有优先撑满整个父容器
- 高亮区域坐标仅在
onAppear时获取一次,后续视图布局变化(如横竖屏切换)时坐标不会同步更新
修复后可运行代码
// 调整镂空遮罩路径,先绘制全屏背景,再叠加需要挖空的高亮区域 func HoleShapeMask(in holeRect: CGRect, viewBounds: CGRect) -> Path { return Path { path in // 先添加全屏尺寸的路径 path.addRect(viewBounds) // 再添加需要镂空的高亮区域路径 path.addRect(holeRect) } } struct ContentView: View { @State private var highlightRect: CGRect = .zero @State private var viewBounds: CGRect = .zero var body: some View { GeometryReader { fullGeom in ZStack { // 此处替换为你的实际页面内容,示例用红色背景模拟完整页面 Color.red .ignoresSafeArea() Text("需要高亮的内容") .foregroundColor(.blue) .frame(width: 124, height: 100) .background(Color.white) .overlay( GeometryReader { itemGeom in Color.clear .onAppear { highlightRect = itemGeom.frame(in: .global) } // 适配布局变化时同步更新高亮区域坐标 .onChange(of: itemGeom.frame(in: .global)) { newRect in highlightRect = newRect } } ) } .onAppear { viewBounds = fullGeom.frame(in: .global) } .onChange(of: fullGeom.frame(in: .global)) { newBounds in viewBounds = newBounds } // 叠加半透明黑色遮罩 .overlay( Color.black.opacity(0.8) .frame(maxWidth: .infinity, maxHeight: .infinity) .ignoresSafeArea() // 用eoFill规则镂空高亮区域 .mask( HoleShapeMask(in: highlightRect, viewBounds: viewBounds) .fill(style: FillStyle(eoFill: true)) ) ) } } }
关键修改说明
- 调整
HoleShapeMask逻辑,同时传入全屏尺寸和高亮区域尺寸,先绘制全屏路径再叠加高亮路径,确保奇偶填充规则可以正确计算出镂空区域 - 新增
viewBounds变量存储全屏尺寸,适配横竖屏切换等布局变化场景 - 新增
onChange监听坐标变化,确保高亮区域坐标和全屏尺寸实时更新 - 遮罩层添加
ignoresSafeArea,保证可以覆盖到状态栏、底部安全区等系统区域,实现全屏遮罩效果
内容的提问来源于stack exchange,提问作者ursan526
相关产品推荐
相关产品推荐

