José
José

Reputation: 3164

Make parts of UIView transparent by using a masking layer

I am trying to achieve something like this:

enter image description here

Where the pink part is fixed and the grey area could possibly scroll. I have the following view structure:

enter image description here

I currently have this code:

extension FloatingPoint {

    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }

} 

@IBDesignable
class MaskView: UIView {

    let startAngle: CGFloat = 180
    let endAngle: CGFloat = 0

    override func layoutSubviews() {
        super.layoutSubviews()

        let multiplier: CGFloat = (frame.size.height * 3)
        let maskLayer = CAShapeLayer()
        maskLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width/2, y: frame.size.height * multiplier),
                                  radius: frame.size.height * multiplier,
                                  startAngle: startAngle.degreesToRadians,
                                  endAngle: endAngle.degreesToRadians,
                                  clockwise: true).cgPath

        layer.mask = maskLayer
    }

}

The Mask View is set to have the grey background color.

The problem that I have is that the circular part is not transparent. Changing the color to UIColor.clear will only make the circular part to disappear. Am I fundamentally doing the wrong approach for this? Any help would be appreciated, thanks.

Upvotes: 2

Views: 2073

Answers (1)

Yuchen
Yuchen

Reputation: 33036

First of all, we need to create a subclass from UIView (which you already did). But here is the code that I used:

private extension FloatingPoint {
    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }
}

@IBDesignable class MaskView: UIView {
    let startAngle: CGFloat = 180
    let endAngle: CGFloat = 0

    override func layoutSubviews() {
        super.layoutSubviews()

        // The multiplier determine how big the circle is
        let multiplier: CGFloat = 3.0
        let radius: CGFloat = frame.size.width * multipler
        let maskLayer = CAShapeLayer()
        let arcCenter = CGPoint(x: frame.size.width / 2, y: radius)
        maskLayer.path = UIBezierPath(arcCenter: arcCenter,
                                      radius: radius,
                                      startAngle: startAngle.degreesToRadians,
                                      endAngle: endAngle.degreesToRadians,
                                      clockwise: true).cgPath
        layer.mask = maskLayer
    }
}

Then you can add a MaskView as a subView in the ViewController. Make sure select the view assigned the class MaskView in the storyboard:

Now we have a very simply view hierarchy:

Compiles the code and it's looking great:

If you want a scrollable subview that is masked, add it as a subview of the maskView. Here is how the view hierarchy looks after this:

And finally, this is how it looks running in the simulator:

Ref: https://github.com/yzhong52/Example-LayerMask

Upvotes: 2

Related Questions