Search code examples
iosswiftswiftuiswiftui-view

SwiftUI how to enable interaction on a masked overlay in only the portion that is masked


I have added mask in a overlay to create a spotlight effect in SwiftUI. But at the same time I want the user to interact with the view through that window opening that I have such as sliding a slider or tapping a button while totally block other interactions.

The problem I'm facing is that I'm not able to make interaction through this mask window because it's an overlay. Is there a way I could enable interaction only in the white highlighted area ?

This is how it looks

enter image description here

This is my code to achieve the overlay with highlighted window(spotlight)

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)
                }
        }
}

Solution

  • So I finally found a solution. Instead of doing mask. What I did is use path and make that clipped so that user can interact through clear pixels. Thanks to this answer here.

    func HoleShapeMask(in rect: CGRect) -> Path {
        var shape = Rectangle().path(in: rect)
        shape.addPath(Circle().path(in: rect))
        return shape
    }
    
    struct TestInvertedMask: View {
            let rect = CGRect(x: 0, y: 0, width: 300, height: 100)
            var body: some View {
            Rectangle()
            .fill(Color.blue)
            .frame(width: rect.width, height: rect.height)
            .mask(HoleShapeMask(in: rect)
            .fill(style: FillStyle(eoFill:true)))
        }
    }