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

SwiftUI中如何让遮罩Overlay的镂空区域支持交互?

SwiftUI聚光灯遮罩的交互穿透实现方案

问题描述

我在SwiftUI中通过遮罩实现了聚光灯效果,需求是:用户仅能在遮罩的镂空高亮区域与下方视图(如滑动Slider、点击Button)交互,其他区域完全屏蔽交互。但当前由于遮罩层的存在,镂空区域也无法和底层视图交互,求解决方法。

效果展示

聚光灯效果展示

原实现代码

ZStack {
    VStack {
        Slider(
            value: $speed,
            in: 0...100,
            onEditingChanged: { editing in
                isEditing = editing
            }
        )
        Text("\(speed)")
            .foregroundColor(isEditing ? .red : .blue)
    }
    .padding(.top, 100)

    Rectangle()
        .fill(Color.black.opacity(0.5))
        .mask {
            Rectangle()
                .overlay(alignment: .topLeading) {
                    let rect = CGRect(x: 0, y: 500, width:390, height: 80)
                    RoundedRectangle(cornerRadius: 10, style: .continuous)
                        .frame(width: rect.width, height: rect.height)
                        .offset(y:90)
                        .blendMode(.destinationOut)
                }
        }
}

解决方案

核心是让遮罩层的非镂空区域屏蔽交互,镂空透明区域允许点击穿透到底层视图,只需对遮罩视图做两处修改:

  1. 添加.compositingGroup():将遮罩后的视图组合成一个渲染组,让系统正确识别透明区域(因为使用了blendMode(.destinationOut),必须组化才能让点击测试生效)
  2. 保留.allowsHitTesting(true):让非透明的黑色遮罩区域接收点击,从而屏蔽底层交互,透明镂空区域则自动忽略点击,让事件透传到底层

修改后的完整代码

ZStack {
    VStack {
        Slider(
            value: $speed,
            in: 0...100,
            onEditingChanged: { editing in
                isEditing = editing
            }
        )
        Text("\(speed)")
            .foregroundColor(isEditing ? .red : .blue)
    }
    .padding(.top, 100)

    Rectangle()
        .fill(Color.black.opacity(0.5))
        .mask {
            Rectangle()
                .overlay(alignment: .topLeading) {
                    let rect = CGRect(x: 0, y: 500, width:390, height: 80)
                    RoundedRectangle(cornerRadius: 10, style: .continuous)
                        .frame(width: rect.width, height: rect.height)
                        .offset(y:90)
                        .blendMode(.destinationOut)
                }
        }
        .compositingGroup() // 关键:组化视图,确保透明区域的点击测试正确
        .allowsHitTesting(true)
}

原理说明

SwiftUI中,默认会忽略完全透明区域的点击事件,但当使用blendMode这类修改像素的操作时,必须通过.compositingGroup()将视图及其遮罩打包成一个独立渲染单元,这样系统才能准确判断哪些区域是可交互(非透明)、哪些是可穿透(透明)的。

内容的提问来源于stack exchange,提问作者Asadullah Ali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:47:42