Skip to content

Instantly share code, notes, and snippets.

@mehmetfarhan
Created July 9, 2020 20:36
Show Gist options
  • Save mehmetfarhan/caf211c76352a8520029928bf0b25fcd to your computer and use it in GitHub Desktop.
Save mehmetfarhan/caf211c76352a8520029928bf0b25fcd to your computer and use it in GitHub Desktop.
Scanner Overlay
import AVFoundation
import UIKit
public class ScannerOverlayPreviewLayer: AVCaptureVideoPreviewLayer {
// MARK: - OverlayScannerPreviewLayer
public var cornerLength: CGFloat = 30
public var lineWidth: CGFloat = 6
public var lineColor: UIColor = .white
public var lineCap: CAShapeLayerLineCap = .round
public var maskSize: CGSize = CGSize(width: 200, height: 200)
public var rectOfInterest: CGRect {
metadataOutputRectConverted(fromLayerRect: maskContainer)
}
public override var frame: CGRect {
didSet {
setNeedsDisplay()
}
}
private var maskContainer: CGRect {
CGRect(x: (bounds.width / 2) - (maskSize.width / 2),
y: (bounds.height / 2) - (maskSize.height / 2),
width: maskSize.width, height: maskSize.height)
}
// MARK: - Drawing
public override func draw(in ctx: CGContext) {
super.draw(in: ctx)
// MARK: - Background Mask
let path = CGMutablePath()
path.addRect(bounds)
path.addRoundedRect(in: maskContainer, cornerWidth: cornerRadius, cornerHeight: cornerRadius)
let maskLayer = CAShapeLayer()
maskLayer.path = path
maskLayer.fillColor = backgroundColor
maskLayer.fillRule = .evenOdd
addSublayer(maskLayer)
// MARK: - Edged Corners
if cornerRadius > cornerLength { cornerRadius = cornerLength }
if cornerLength > maskContainer.width / 2 { cornerLength = maskContainer.width / 2 }
let upperLeftPoint = CGPoint(x: maskContainer.minX, y: maskContainer.minY)
let upperRightPoint = CGPoint(x: maskContainer.maxX, y: maskContainer.minY)
let lowerRightPoint = CGPoint(x: maskContainer.maxX, y: maskContainer.maxY)
let lowerLeftPoint = CGPoint(x: maskContainer.minX, y: maskContainer.maxY)
let upperLeftCorner = UIBezierPath()
upperLeftCorner.move(to: upperLeftPoint.offsetBy(dx: 0, dy: cornerLength))
upperLeftCorner.addArc(withCenter: upperLeftPoint.offsetBy(dx: cornerRadius, dy: cornerRadius),
radius: cornerRadius, startAngle: .pi, endAngle: 3 * .pi / 2, clockwise: true)
upperLeftCorner.addLine(to: upperLeftPoint.offsetBy(dx: cornerLength, dy: 0))
let upperRightCorner = UIBezierPath()
upperRightCorner.move(to: upperRightPoint.offsetBy(dx: -cornerLength, dy: 0))
upperRightCorner.addArc(withCenter: upperRightPoint.offsetBy(dx: -cornerRadius, dy: cornerRadius),
radius: cornerRadius, startAngle: 3 * .pi / 2, endAngle: 0, clockwise: true)
upperRightCorner.addLine(to: upperRightPoint.offsetBy(dx: 0, dy: cornerLength))
let lowerRightCorner = UIBezierPath()
lowerRightCorner.move(to: lowerRightPoint.offsetBy(dx: 0, dy: -cornerLength))
lowerRightCorner.addArc(withCenter: lowerRightPoint.offsetBy(dx: -cornerRadius, dy: -cornerRadius),
radius: cornerRadius, startAngle: 0, endAngle: .pi / 2, clockwise: true)
lowerRightCorner.addLine(to: lowerRightPoint.offsetBy(dx: -cornerLength, dy: 0))
let bottomLeftCorner = UIBezierPath()
bottomLeftCorner.move(to: lowerLeftPoint.offsetBy(dx: cornerLength, dy: 0))
bottomLeftCorner.addArc(withCenter: lowerLeftPoint.offsetBy(dx: cornerRadius, dy: -cornerRadius),
radius: cornerRadius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
bottomLeftCorner.addLine(to: lowerLeftPoint.offsetBy(dx: 0, dy: -cornerLength))
let combinedPath = CGMutablePath()
combinedPath.addPath(upperLeftCorner.cgPath)
combinedPath.addPath(upperRightCorner.cgPath)
combinedPath.addPath(lowerRightCorner.cgPath)
combinedPath.addPath(bottomLeftCorner.cgPath)
let shapeLayer = CAShapeLayer()
shapeLayer.path = combinedPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = lineWidth
shapeLayer.lineCap = lineCap
addSublayer(shapeLayer)
}
}
internal extension CGPoint {
// MARK: - CGPoint+offsetBy
func offsetBy(dx: CGFloat, dy: CGFloat) -> CGPoint {
var point = self
point.x += dx
point.y += dy
return point
}
}
// MARK: - Usage
// At first setup your AVCaptureSession, then initiate the ScannerOverlay with it,
// and after that add it as a sublayer to your view.
let scannerOverlayPreviewLayer = ScannerOverlayPreviewLayer(session: captureSession)
scannerOverlayPreviewLayer.frame = view.bounds
scannerOverlayPreviewLayer.maskSize = CGSize(width: 200, height: 200)
scannerOverlayPreviewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(scannerOverlayPreviewLayer)
@mehmetfarhan
Copy link
Author

84567872-f3bc2d80-ad7b-11ea-97d0-6f6684383345

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment