Written by
Linh Vo
on
on
Using CAShapeLayer and UIBezierPath to mask UIView holes
The trick is pretty straightforward — basically we need to get the path from our overlayView
using UIBezierPath(rect: overlayView.bounds)
, then set fillRule
from our CAShapeLayer
to .evenOdd
and then append the path of the view we want to crop. We could either do point/point…line/line using path.append
or we can simply add a subvview to the overlayView
with the size we want to crop and then append its bounds to the path.
import UIKit
class HoleInViewController: UIViewController {
@IBOutlet weak var overlayView: UIView!
let backgroundView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.black.withAlphaComponent(0.7)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(backgroundView)
NSLayoutConstraint.activate([
backgroundView.topAnchor.constraint(equalTo: view.topAnchor),
backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
overlayView.layer.cornerRadius = overlayView.bounds.width / 2
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Create the initial layer from the view bounds.
let maskLayer = CAShapeLayer()
maskLayer.frame = backgroundView.bounds
maskLayer.fillColor = UIColor.green.cgColor
maskLayer.fillRule = .evenOdd
// Create the path.
let path = UIBezierPath(rect: backgroundView.bounds)
// Append the overlay image to the path so that it is subtracted.
path.append(UIBezierPath(roundedRect: overlayView.frame, cornerRadius: overlayView.bounds.width / 2))
maskLayer.path = path.cgPath
// Set the mask of the view.
backgroundView.layer.mask = maskLayer
}
}