The key observation is that a mask
will reveal the underlying view/layer based upon the alpha channel of the mask. As the documentation says:
The view’s alpha channel determines how much of the view’s content and background shows through. Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content.
Thus, a mask yields your scenario “B”.
You ask:
How do I create a mask like Image A with two UIView
?
In short, this sort of inverted masking is not something you can just do with view’s and applying a corner radius of one of their layers. We would generally achieve that sort of inverse mask by masking the view’s layer
with a CAShapeLayer
, where you can draw whatever shape you want.
let origin = CGPoint(x: 100, y: 100)
let size = CGSize(width: 200, height: 300)
let cornerRadius: CGFloat = 100
let blueView = UIView(frame: CGRect(origin: origin, size: size))
blueView.backgroundColor = .blue
view.addSubview(blueView)
let rect = CGRect(origin: .zero, size: size)
let path = UIBezierPath(rect: rect)
path.append(UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius))
let maskLayer = CAShapeLayer()
maskLayer.fillRule = .evenOdd
maskLayer.path = path.cgPath
blueView.layer.mask = maskLayer
But, frankly, a UIViewController
probably shouldn’t be reaching into a UIView
and adjusting its layer’s mask. I would define a CornersView
:
class CornersView: UIView {
var cornerRadius: CGFloat? { didSet { setNeedsLayout() } }
override func layoutSubviews() {
super.layoutSubviews()
let radius = cornerRadius ?? min(bounds.width, bounds.height) / 2
let path = UIBezierPath(rect: bounds)
path.append(UIBezierPath(roundedRect: bounds, cornerRadius: radius))
let maskLayer = CAShapeLayer()
maskLayer.fillRule = .evenOdd
maskLayer.path = path.cgPath
layer.mask = maskLayer
}
}
By putting the rounding logic in layoutSubviews
, that means that this rounding will be applied automatically regardless of the size. Then, you would use it like so:
let blueView = CornersView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
blueView.backgroundColor = .blue
view.addSubview(blueView)
If you use storyboards, you might even make CornersView
an @IBDesignable
and make cornerRadius
an @IBInspectable
.