SwiftUI 添加倒置遮罩

Gar*_*ner 18 swift swiftui

我正在尝试为两个形状添加遮罩,以便第二个形状遮住第一个形状。如果我执行类似的操作Circle().mask(Circle().offset(…)),则会产生相反的效果:防止第一个圆圈外的任何内容可见。

对于UIView答案就在这里:在drawRect中的iOS转化面具

然而,试图实现这SwiftUI没有 UIView躲开我。我尝试实现一个 InvertedShape,然后我可以将其用作掩码:

struct InvertedShape<OriginalType: Shape>: Shape {
    let originalShape: OriginalType

    func path(in rect: CGRect) -> Path {
        let mutableOriginal = originalShape.path(in: rect).cgPath.mutableCopy()!
        mutableOriginal.addPath(Path(rect).cgPath)
        return Path(mutableOriginal)
            .evenOddFillRule()
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,SwiftUI 路径没有addPath(Path)(因为它们是不可变的)或evenOddFillRule(). 您可以访问路径的 CGPath 并制作可变副本,然后添加两个路径,但是,evenOddFillRule需要CGLayerCGPath.而不是. 所以除非我能到达 CGLayer,否则我不确定如何继续。

这是斯威夫特 5。

Ser*_*kov 23

使用.blendMode修饰符

ZStack {
      Rectangle() // destination
      Circle()    // source
        .blendMode(.destinationOut)
    }
    .compositingGroup()
Run Code Online (Sandbox Code Playgroud)


Asp*_*eri 22

这是仅通过 SwiftUI 创建倒置遮罩的可能方法的演示(例如在视图中打孔)

SwiftUI 孔掩码、反向掩码

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)))
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,我知道它不相关,但是是否可以将此形状添加到 ZStack 并允许用户仅通过 Hole 与后台内容进行交互? (2认同)

Vla*_*ego 14

这是另一种方法,它更 Swiftly。

诀窍是使用:

YourMaskView()
   .compositingGroup()
   .luminanceToAlpha() 
Run Code Online (Sandbox Code Playgroud)

maskedView.mask(YourMaskView())

只需创建具有黑白形状的蒙版,黑色将是透明的,白色是不透明的,介于两者之间的任何东西都将是半透明的。

.compositingView(),类似于.drawingGroup(),栅格化视图(将其转换为位图纹理)。顺便说一句,当您.blur或执行任何其他像素级操作时,也会发生这种情况。

.luminanceToAlpha() 获取 RGB 亮度级别(我猜是通过平均 RGB 值),并将它们映射到位图的 Alpha(不透明度)通道。

  • 当 YourMaskView 为黑白时,这非常有用,谢谢 (2认同)

Tam*_*gel 10

根据本文.reverseMask您可以使用以下修饰符来代替.mask. 我修改了它以支持 iOS 13 及更高版本。

extension View {
    @inlinable func reverseMask<Mask: View>(
        alignment: Alignment = .center,
        @ViewBuilder _ mask: () -> Mask
    ) -> some View {
            self.mask(
                ZStack {
                    Rectangle()

                    mask()
                        .blendMode(.destinationOut)
                }
            )
        }
}
Run Code Online (Sandbox Code Playgroud)

用法:

ViewToMask()
.reverseMask {
    MaskView()
}
Run Code Online (Sandbox Code Playgroud)


Jac*_*sen 7

在接受的答案中使用掩码是一种很好的方法。不幸的是,掩码不会影响命中测试。可以通过以下方式制作带孔的形状。

extension Path {
    var reversed: Path {
        let reversedCGPath = UIBezierPath(cgPath: cgPath)
            .reversing()
            .cgPath
        return Path(reversedCGPath)
    }
}

struct ShapeWithHole: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Rectangle().path(in: rect)
        let hole = Circle().path(in: rect).reversed
        path.addPath(hole)
        return path
    }
}
Run Code Online (Sandbox Code Playgroud)

诀窍是反转孔的路径。不幸的Path是(还)不支持开箱即用的反转路径,因此扩展(使用UIBezierPath)。然后该形状可用于剪辑和命中测试目的:

struct MaskedView: View {

    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 300, height: 100)
            .clipShape(ShapeWithHole())    // clips or masks the view
            .contentShape(ShapeWithHole()) // needed for hit-testing
    }
}
Run Code Online (Sandbox Code Playgroud)