0

I am trying to create a QR Reader. For that I am showing a rectOfInterest with some CALayer for visual representation. I want to show a box with some border at the corners and black background with some opacity so hide the other view from the AVCaptureVideoPreviewLayer. What I have achieved till now looks like this:

Screenshot

As you can see the CALayer is there but I want to cut the box portion of the layer so that that blackish thing does not come there. The code I am using to do this is like below:

func createTransparentLayer()->CALayer{
        let shape = CALayer()
        shape.frame = self.scanView.layer.bounds
        shape.backgroundColor = UIColor.black.cgColor
        shape.opacity = 0.7
        return shape
    }

I looked into other questions for this, seems like you have the mask the layer with the cut portion. So I subclassed the CALayer and cleared the context in drawInContext and set the mask property of the super layer to this. After that I get nothing. Everything is invisible there. What is wrong in this?

The code I tried is this:

class TransparentLayer: CALayer {
    override func draw(in ctx: CGContext) {
        self.backgroundColor = UIColor.black.cgColor
        self.opacity = 0.7
        self.isOpaque = true
        ctx.clear(CGRect(x: superlayer!.frame.size.width / 2 - 100, y: superlayer!.frame.size.height / 2 - 100, width: 200, height: 200))
    }
}

then set the mask property like this:

override func viewDidLayoutSubviews() {
        self.rectOfInterest = CGRect(x: self.scanView.layer.frame.size.width / 2 - 100, y: self.scanView.layer.frame.size.height / 2 - 100, width: 200, height: 200)
        scanView.rectOfInterest = self.rectOfInterest
        let shapeLayer = self.createFrame()
        scanView.doInitialSetup()
        self.scanView.layer.mask = self.createTransparentLayer()
        self.scanView.layer.addSublayer(shapeLayer)
    }

here the shapeLayer is the bordered corner in the screenshot. How can I achieve this?

Anuran Barman
  • 1,556
  • 2
  • 16
  • 31

1 Answers1

1

I have added view in my view controller which is centre align vertically and horizontally. Also fixed height and width to 200. Then created extension of UIView and added following code:

extension UIView {

    func strokeBorder() {

        self.backgroundColor = .clear
        self.clipsToBounds = true

        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = UIBezierPath(rect: self.bounds).cgPath
        self.layer.mask = maskLayer

        let line = NSNumber(value: Float(self.bounds.width / 2))

        let borderLayer = CAShapeLayer()
        borderLayer.path = maskLayer.path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = UIColor.white.cgColor
        borderLayer.lineDashPattern = [line]
        borderLayer.lineDashPhase = self.bounds.width / 4
        borderLayer.lineWidth = 10
        borderLayer.frame = self.bounds
        self.layer.addSublayer(borderLayer)
    }
}

Use:

By using outlet of view and call method to set border as you have request.

self.scanView.strokeBorder()

To clear the backgroundColor respective to mask view, I have added following code to clear it.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    self.scanView.strokeBorder()

    self.backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.5)

    // Draw a graphics with a mostly solid alpha channel
    // and a square of "clear" alpha in there.
    UIGraphicsBeginImageContext(self.backgroundView.bounds.size)
    let cgContext = UIGraphicsGetCurrentContext()
    cgContext?.setFillColor(UIColor.white.cgColor)
    cgContext?.fill(self.backgroundView.bounds)
    cgContext?.clear(CGRect(x:self.scanView.frame.origin.x, y:self.scanView.frame.origin.y, width: self.scanView.frame.width, height: self.scanView.frame.height))
    let maskImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    // Set the content of the mask view so that it uses our
    // alpha channel image
    let maskView = UIView(frame: self.backgroundView.bounds)
    maskView.layer.contents = maskImage?.cgImage
    self.backgroundView.mask = maskView
}

Output:

I'm not using camera in background.

Mask Background with Clear Colour

Sagar Chauhan
  • 5,715
  • 2
  • 22
  • 56