Home > Software design >  How to make a transparent hole in UIVisualEffectView?
How to make a transparent hole in UIVisualEffectView?

Time:06-03

I have a simple UIViewController with UIVisualEffectView presented over another controller using overCurrentContext.

enter image description here

and it is fine.

now I try to make a hole inside that view the following way:

class CoverView: UIView {
    private let blurView: UIVisualEffectView = {
        UIVisualEffectView(effect: UIBlurEffect(style: .dark))
    }()

    // MARK: - Internal

    func setup() {
        addSubview(blurView)
        blurView.snp.makeConstraints { maker in
            maker.edges.equalToSuperview()
        }
        blurView.makeClearHole(rect: CGRect(x: 100, y: 100, width: 100, height: 230))
    }
}

extension UIView {
    func makeClearHole(rect: CGRect) {
        let maskLayer = CAShapeLayer()
        maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
        maskLayer.fillColor = UIColor.black.cgColor

        let pathToOverlay = UIBezierPath(rect: bounds)
        pathToOverlay.append(UIBezierPath(rect: rect))
        pathToOverlay.usesEvenOddFillRule = true
        maskLayer.path = pathToOverlay.cgPath

        layer.mask = maskLayer
    }
}

But the effect is reversed than I expected, why?

enter image description here

I need everything around blurred the way how rectangle currently is. And the rect inside should be transparent.

EDIT::

I have studied everything from comments below, and tried another answer, but result is still the same. Why?;) I have no idea what is wrong.

private func makeClearHole(rect: CGRect) {
    let maskLayer = CAShapeLayer()
    maskLayer.fillColor = UIColor.black.cgColor

    let pathToOverlay = CGMutablePath()
    pathToOverlay.addRect(blurView.bounds)
    pathToOverlay.addRect(rect)
    maskLayer.path = pathToOverlay
    maskLayer.fillRule = .evenOdd
    blurView.layer.mask = maskLayer
}

CodePudding user response:

Well I tested your code and the original code with makeClearHole function in the extension works fine! The problem lies somewhere else.

1- Change the CoverView as following*

class CoverView: UIView {
    private lazy var blurView: UIVisualEffectView = {
        let bv = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
        bv.frame = bounds
        return bv
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Internal

    func setup() {
        addSubview(blurView)
        blurView.snp.makeConstraints { maker in
            maker.edges.equalToSuperview()
        }
        blurView.makeClearHole(rect: CGRect(x: 100, y: 100, width: 100, height: 230))
    }
}

2- Give a frame to coverView in your view controller The view controller you have is different. But you should give the CoverView instance a frame. This is how: (again, this is how I tested but your view controller is definitely different)

class ViewController: UIViewController {

    var label: UILabel!
    var coverView: CoverView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        label = UILabel()
        label.text = "HELLO WORLD"
        label.font = UIFont.systemFont(ofSize: 40, weight: .black)
        
        
        coverView = CoverView(frame: CGRect(x: 20, y: 200, width: 300, height: 400))
        
        view.addSubview(label)
        view.addSubview(coverView)
        
        label.snp.makeConstraints { make in
            make.center.equalTo(view)
        }
        
        coverView.snp.makeConstraints { make in
            make.width.equalTo(coverView.bounds.width)
            make.height.equalTo(coverView.bounds.height)
            make.leading.equalTo(view).offset(coverView.frame.minX)
            make.top.equalTo(view).offset(coverView.frame.minY)
        }
    }


}

** Result**

Transparent rect inside blur view

  • Related