I was making a progress circle, and I want its track path to have a blur effect, is there any way to achieve that?
This is what the original track looks like(the track path is transparent, I want it to be blurred)
let outPutViewFrame = CGRect(x: 0, y: 0, width: 500, height: 500)
let circleRadius: CGFloat = 60
let circleViewCenter = CGPoint(x: outPutViewFrame.width / 2 , y: outPutViewFrame.height / 2)
let circleView = UIView()
let progressWidth: CGFloat = 8
circleView.frame.size = CGSize(width: (circleRadius + progressWidth) * 2, height: (circleRadius + progressWidth) * 2)
circleView.center = circleViewCenter
let circleTrackPath = UIBezierPath(arcCenter: CGPoint(x: circleView.frame.width / 2, y: circleView.frame.height / 2), radius: circleRadius, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
let blur = UIBlurEffect(style: .light)
let blurEffect = UIVisualEffectView(effect: blur)
blurEffect.frame = circleView.bounds
blurEffect.mask(withPath: circleTrackPath, inverse: false)
extension UIView {
func mask(withPath path: UIBezierPath, inverse: Bool = false) {
let maskLayer = CAShapeLayer()
if inverse {
path.append(UIBezierPath(rect: self.bounds))
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
}
maskLayer.path = path.cgPath
maskLayer.lineWidth = 5
maskLayer.lineCap = CAShapeLayerLineCap.round
self.layer.mask = maskLayer
}
}
Set maskLayer.fillRule
to evenOdd
, even when not inversed
.
if inverse {
path.append(UIBezierPath(rect: self.bounds))
}
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
create the circleTrackPath
by using a big circle and a smaller circle.
let circleCenter = CGPoint(x: circleView.frame.width / 2, y: circleView.frame.height / 2)
let circleTrackPath = UIBezierPath(ovalIn:
CGRect(origin: circleCenter, size: .zero)
.insetBy(dx: circleRadius, dy: circleRadius))
// smaller circle
circleTrackPath.append(CGRect(origin: circleCenter, size: .zero)
.insetBy(dx: circleRadius * 0.8, dy: circleRadius * 0.8))
Set circleTrackPath.usesEvenOddFillRule
to true:
circleTrackPath.usesEvenOddFillRule = true
Now you have a blurred full circle. The non-blurred arc part can be implemented as another sublayer.
Here is a MCVE that you can paste into a playground:
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
// change this to a view of your choice
let image = UIImageView(image: UIImage(named: "my_image"))
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .light))
container.addSubview(image)
blur.frame = image.frame
container.addSubview(blur)
let outer = image.bounds.insetBy(dx: 30, dy: 30)
let path = UIBezierPath(ovalIn: outer)
path.usesEvenOddFillRule = true
path.append(UIBezierPath(ovalIn: outer.insetBy(dx: 10, dy: 10)))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
maskLayer.fillRule = .evenOdd
blur.layer.mask = maskLayer
container // <--- playground quick look this
Using my profile pic as the background, this produces: