I would like to draw mask to around of crop rectangle that it has grips in corners and I'm trying to fill inside the rectangle with combined UIBezierPath like: 4 circle and 1 rectangle than assigning this path to CAShapeLayer to use it as sublayer on my view. So far so good it's filling desired area just overlapped parts (when we appending circles on rectangle) not filled (quarter triangle in all corner), so I tried using combinedPath.usesEvenOddFillRule = false
- combinedPath.fill()
- combinedPath.close()
but none of them worked with me! So I need to fill these parts also someone can help me please?
You can check the image from this link
My code :
// draw mask
let path = UIBezierPath(rect: viewBackground.bounds)
let maskRect = CGRect(x: imgView.frame.origin.x + roiRect.minX * rate, y: imgView.frame.origin.y + roiRect.minY * rate, width: roiRect.width * rate, height: roiRect.height * rate)
// First apth is for rectangle
let rectPath = UIBezierPath(rect: maskRect)
path.append(rectPath)
// and these paths for corners
let cornerMaskCircleSize: CGFloat = circleSize/2
let maskTopLeftCornerPath = UIBezierPath(ovalIn: CGRect(x: maskRect.minX - cornerMaskCircleSize/2, y: maskRect.minY - cornerMaskCircleSize/2, width: cornerMaskCircleSize, height: cornerMaskCircleSize))
let maskTopRightCornerPath = UIBezierPath(ovalIn: CGRect(x: maskRect.maxX - cornerMaskCircleSize/2, y: maskRect.minY - cornerMaskCircleSize/2, width: cornerMaskCircleSize, height: cornerMaskCircleSize))
let maskBottomRightCornerPath = UIBezierPath(ovalIn: CGRect(x: maskRect.maxX - cornerMaskCircleSize/2, y: maskRect.maxY - cornerMaskCircleSize/2, width: cornerMaskCircleSize, height: cornerMaskCircleSize))
let maskBottomLeftCornerPath = UIBezierPath(ovalIn: CGRect(x: maskRect.minX - cornerMaskCircleSize/2, y: maskRect.maxY - cornerMaskCircleSize/2, width: cornerMaskCircleSize, height: cornerMaskCircleSize))
// Combining all of them in one path
path.append(maskTopLeftCornerPath)
path.append(maskTopRightCornerPath)
path.append(maskBottomRightCornerPath)
path.append(maskBottomLeftCornerPath)
path.usesEvenOddFillRule = true
path.fill()
path.close()
cropMask.removeFromSuperlayer()
cropMask.bounds = viewBackground.frame
cropMask.position = viewBackground.center
cropMask.path = path.cgPath
cropMask.fillRule = .evenOdd
cropMask.fillColor = UIColor.init(hexString: "#212f41").cgColor
cropMask.opacity = 0.6
viewBackground.layer.addSublayer(cropMask)
After 5 hours research finally I found this extension and it fixed my problem with different way, this extension it helps for drawing three quarter circle so I used it to draw circles that empty from rectangle corners side.
And here is an example code for usage :
fileprivate func drawMask() {
let rectPath = UIBezierPath(rect: CGRect(x: 100, y: 200, width: 175, height: 150))
let leftTopCornerPath = UIBezierPath(quarterCircleCentre: CGPoint(x: 100, y: 200), radius: 10, quadrant: .BottomRightThreeQuarterCircle)
let rightTopCornerPath = UIBezierPath(quarterCircleCentre: CGPoint(x: 275, y: 200), radius: 10, quadrant: .BottomLeftThreeQuarterCircle)
let leftBottomCornerPath = UIBezierPath(quarterCircleCentre: CGPoint(x: 100, y: 350), radius: 10, quadrant: .TopRightThreeQuarterCircle)
let rightBottomCornerPath = UIBezierPath(quarterCircleCentre: CGPoint(x: 275, y: 350), radius: 10, quadrant: .TopLeftThreeQuarterCircle)
let combined = UIBezierPath(rect: self.view.frame)
combined.append(rectPath)
combined.append(leftTopCornerPath)
combined.append(rightTopCornerPath)
combined.append(leftBottomCornerPath)
combined.append(rightBottomCornerPath)
let maskLayer = CAShapeLayer()
maskLayer.bounds = self.view.bounds
maskLayer.position = self.view.center
maskLayer.path = combined.cgPath
maskLayer.fillRule = .evenOdd
maskLayer.fillColor = UIColor.black.cgColor
maskLayer.opacity = 0.5
self.view.layer.addSublayer(maskLayer)
}
Extension :
extension UIBezierPath {
enum QuarterCircleQuadrant {
case TopLeft, BottomLeft, TopRight, BottomRight, TopLeftThreeQuarterCircle, BottomRightThreeQuarterCircle, BottomLeftThreeQuarterCircle, TopRightThreeQuarterCircle
var startAngle: CGFloat {
switch self {
case .BottomLeft:
return CGFloat(Double.pi)
case .BottomLeftThreeQuarterCircle:
return CGFloat(Double.pi)
case .TopLeft:
return CGFloat(Double.pi)
case .BottomRight:
return 0
case .TopRight:
return 0
case .TopRightThreeQuarterCircle:
return 0
case .TopLeftThreeQuarterCircle:
return CGFloat(Double.pi)
case .BottomRightThreeQuarterCircle:
return 0
}
}
var endAngle: CGFloat {
switch self {
case .BottomLeft:
return CGFloat(Double.pi/2)
case .BottomLeftThreeQuarterCircle:
return CGFloat(Double.pi/2)
case .TopLeft:
return CGFloat(Double.pi) * 1.5
case .BottomRight:
return CGFloat(Double.pi/2)
case .TopRight:
return CGFloat(Double.pi) * 1.5
case .TopRightThreeQuarterCircle:
return CGFloat(Double.pi) * 1.5
case .TopLeftThreeQuarterCircle:
return CGFloat(Double.pi) * 1.5
case .BottomRightThreeQuarterCircle:
return CGFloat(Double.pi/2)
}
}
var clockwise: Bool {
switch self {
case .BottomLeft:
return false
case .TopLeft:
return true
case .BottomRight:
return true
case .TopRight:
return false
case .BottomLeftThreeQuarterCircle:
return true
case .TopRightThreeQuarterCircle:
return true
case .TopLeftThreeQuarterCircle:
return false
case .BottomRightThreeQuarterCircle:
return false
}
}
}
convenience init(quarterCircleCentre centre:CGPoint, radius:CGFloat, quadrant:QuarterCircleQuadrant)
{
self.init()
self.move(to: CGPoint(x: centre.x, y:centre.y))
self.addArc(withCenter: centre, radius:radius, startAngle:quadrant.startAngle, endAngle: quadrant.endAngle, clockwise:quadrant.clockwise)
self.close()
}
}
Result image link