一部をくり抜いたオーバーレイを表示する方法

背景の一部を表示させたまま、半透明のオーバーレイを表示させたい事があったので、その時の実装した内容のメモです📝

具体的には下記のgifのような挙動の実現方法です。
ボタンをタップすると、背景に表示された緑の四角が見えるようにくり抜かれたオーバーレイを表示して、オーバーレイのどこかをタップすると、それが消えるというもの。

コードはこちらに置いてます。

f:id:muchan611:20191001002450g:plain

一部をくり抜いたオーバーレイは OverlayClippedViewController で実装していて、コードはこちら

private lazy var backgroundLayer: CALayer = {
    let clippedViewWidth: CGFloat = 220
    let clippedViewHeight: CGFloat = 220
    let backgroundLayer = CALayer()
    backgroundLayer.bounds = view.bounds
    backgroundLayer.position = view.center
    backgroundLayer.backgroundColor = UIColor.black.cgColor
    backgroundLayer.opacity = 0.5

    let maskLayer = CAShapeLayer()
    maskLayer.bounds = backgroundLayer.bounds
    let clippingViewRect =  CGRect(x: (view.bounds.width - clippedViewWidth) / 2, y: 190, width: clippedViewWidth, height: clippedViewHeight)
    let path =  UIBezierPath(roundedRect: clippingViewRect, cornerRadius: 5.0)
    path.append(UIBezierPath(rect: maskLayer.bounds))

    maskLayer.fillColor = UIColor.black.cgColor
    maskLayer.path = path.cgPath
    maskLayer.position = view.center
    maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
    backgroundLayer.mask = maskLayer
    return backgroundLayer
}()

maskLayer が塗りつぶしを指定するLayerで、 maskLayer.fillRule = CAShapeLayerFillRule.evenOdd が実際にくり抜く(塗りつぶさない)指定をしている箇所ですね。

半透明のLayerである backgroundLayer のmaskにくり抜き用のmaskLayerを指定する事で、該当箇所がくり抜かれた状態で表示できます✂️

あまり画像をクリップする等しないので、また同じような事を実現したくなった時のための備忘録でした✍️