Animate CAShapeLayer with circle UIBezierPath and CABasicAnimation - ios

I'd like to animate a circle from angle 0 to 360 degrees in 15 sec.
The animation is weird. I know this is probably a start/end angle issue, I already faced that kind of problem with circle animations, but I don't know how to solve this one.
var circle_layer=CAShapeLayer()
var circle_anim=CABasicAnimation(keyPath: "path")
func init_circle_layer(){
let w=circle_view.bounds.width
let center=CGPoint(x: w/2, y: w/2)
//initial path
let start_angle:CGFloat = -0.25*360*CGFloat.pi/180
let initial_path=UIBezierPath(arcCenter: center, radius: w/2, startAngle: start_angle, endAngle: start_angle, clockwise: true)
initial_path.addLine(to: center)
//final path
let end_angle:CGFloat=start_angle+360*CGFloat(CGFloat.pi/180)
let final_path=UIBezierPath(arcCenter: center, radius: w/2, startAngle: start_angle, endAngle: end_angle, clockwise: true)
final_path.addLine(to: center)
//init layer
circle_layer.path=initial_path.cgPath
circle_layer.fillColor=UIColor(hex_code: "EA535D").cgColor
circle_view.layer.addSublayer(circle_layer)
//init anim
circle_anim.duration=15
circle_anim.fromValue=initial_path.cgPath
circle_anim.toValue=final_path.cgPath
circle_anim.isRemovedOnCompletion=false
circle_anim.fillMode=kCAFillModeForwards
circle_anim.delegate=self
}
func start_circle_animation(){
circle_layer.add(circle_anim, forKey: "circle_anim")
}
I want to start on top at 0 degrees and finish on top after a full tour:

You can't easily animate the fill of a UIBezierPath (or at least without introducing weird artifacts except in nicely controlled situations). But you can animate the strokeEnd of a path of the CAShapeLayer. And if you make the line width of the stroked path really wide (i.e. the radius of the final circle), and set the radius of the path to be half of that of the circle, you get something like what you're looking for.
private var circleLayer = CAShapeLayer()
private func configureCircleLayer() {
let radius = min(circleView.bounds.width, circleView.bounds.height) / 2
circleLayer.strokeColor = UIColor(hexCode: "EA535D").cgColor
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.lineWidth = radius
circleView.layer.addSublayer(circleLayer)
let center = CGPoint(x: circleView.bounds.width/2, y: circleView.bounds.height/2)
let startAngle: CGFloat = -0.25 * 2 * .pi
let endAngle: CGFloat = startAngle + 2 * .pi
circleLayer.path = UIBezierPath(arcCenter: center, radius: radius / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true).cgPath
circleLayer.strokeEnd = 0
}
private func startCircleAnimation() {
circleLayer.strokeEnd = 1
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 15
circleLayer.add(animation, forKey: nil)
}
For ultimate control, when doing complex UIBezierPath animations, you can use CADisplayLink, avoiding artifacts that can sometimes result when using CABasicAnimation of the path:
private var circleLayer = CAShapeLayer()
private weak var displayLink: CADisplayLink?
private var startTime: CFTimeInterval!
private func configureCircleLayer() {
circleLayer.fillColor = UIColor(hexCode: "EA535D").cgColor
circleView.layer.addSublayer(circleLayer)
updatePath(percent: 0)
}
private func startCircleAnimation() {
startTime = CACurrentMediaTime()
displayLink = {
let _displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
_displayLink.add(to: .current, forMode: .commonModes)
return _displayLink
}()
}
#objc func handleDisplayLink(_ displayLink: CADisplayLink) { // the #objc qualifier needed for Swift 4 #objc inference
let percent = CGFloat(CACurrentMediaTime() - startTime) / 15.0
updatePath(percent: min(percent, 1.0))
if percent > 1.0 {
displayLink.invalidate()
}
}
private func updatePath(percent: CGFloat) {
let w = circleView.bounds.width
let center = CGPoint(x: w/2, y: w/2)
let startAngle: CGFloat = -0.25 * 2 * .pi
let endAngle: CGFloat = startAngle + percent * 2 * .pi
let path = UIBezierPath()
path.move(to: center)
path.addArc(withCenter: center, radius: w/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.close()
circleLayer.path = path.cgPath
}
Then you can do:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
configureCircleLayer()
startCircleAnimation()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
displayLink?.invalidate() // to avoid displaylink keeping a reference to dismissed view during animation
}
That yields:

Related

CABasicAnimation Stroke End Animation Percentage

I have a custom progress view that animates based on Character Count. (similar to twitter)
However, my calculation seems to be off. I want the stroke layer to circle around based on the percentage.
Here's my code:
class CharacterCountView: UIView {
private let progressLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func animateToValue(value: Double) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.toValue = value
animation.duration = 0.5
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
progressLayer.add(animation, forKey: "strokeAnimation")
progressLayer.strokeEnd = value
if value > 1 {
print("100% REACHED")
}
}
}
extension CharacterCountView {
fileprivate func setUpView() {
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(animateToValue)))
let trackLayer = CAShapeLayer()
trackLayer.frame = bounds
let path = UIBezierPath(arcCenter: trackLayer.position, radius: 8, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = path.cgPath
trackLayer.strokeColor = UIColor.systemGray2.cgColor
trackLayer.lineWidth = 1.5
trackLayer.fillColor = Color.background.cgColor
trackLayer.lineCap = .round
layer.addSublayer(trackLayer)
progressLayer.frame = bounds
progressLayer.path = path.cgPath
progressLayer.strokeColor = Color.accent.cgColor
progressLayer.lineWidth = 1.5
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.lineCap = .round
progressLayer.strokeEnd = 0
layer.addSublayer(progressLayer)
}
}
In my Text Did Change Function:
func textDidChange(to value: String) {
if value.isEmpty || value.count > 280 {
postBarButtonItem.isEnabled = false
} else {
postBarButtonItem.isEnabled = true
}
let characterCount = Double(value.count)
let percentage = characterCount / 280
print(percentage)
createPostInputAccessoryView.characterCountView.animateToValue(value: percentage)
}
Its topping out at around 70% what is wrong with my calculation?
Your endAngle is wrong...
Let's make it a little more "readable":
let path = UIBezierPath(arcCenter: trackLayer.position,
radius: 8,
startAngle: -CGFloat.pi / 2,
endAngle: 2 * CGFloat.pi,
clockwise: true)
This: startAngle: -CGFloat.pi / 2 is minus 90-degrees, or 12 o'clock on the circle.
But this: endAngle: 2 * CGFloat.pi is 360-degrees, or 3 o'clock
So you have 450-degrees instead of 360-degrees.
I find it helps to use multipliers only, instead of mixing multiplication and division:
let path = UIBezierPath(arcCenter: trackLayer.position,
radius: 8,
startAngle: -CGFloat.pi * 0.5,
endAngle: CGFloat.pi * 1.5,
clockwise: true)
Now we know we're going from minus 90-degrees to 270-degrees ... for 360-degrees total.

iOS: Increase animation by click a button

I have the following animation, I need to implement a code, by clicking on a button the animation increased by one until a value that I want to reach it.
I try the following code:
let strokeIt = CABasicAnimation(keyPath: "strokeEnd")
let timeLeftShapeLayer = CAShapeLayer()
let bgShapeLayer = CAShapeLayer()
override func viewDidLoad() {
drawBgShape()
drawTimeLeftShape()
}
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
bgShapeLayer.strokeColor = UIColor.white.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 15
view.layer.addSublayer(bgShapeLayer)
}
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY), radius:
100, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 15
view.layer.addSublayer(timeLeftShapeLayer)
}
and when click a button :
var x = 0.0
#IBAction func click(_ sender: Any) {
strokeIt.fromValue = x
strokeIt.toValue = x + 0.1
strokeIt.duration = 0.25
timeLeftShapeLayer.add(strokeIt, forKey: nil)
x = x + 0.1
}
How can I achieve that?
You don't need to use an explicit animation to achieve this.
For some properties of CALayer, Core Animation will add an implicit animation for you. The strokeEnd property is one of these.
This means that Core Animation will automatically animate any changes to a layer's strokeEnd property. You don't need to create an animation object and add it yourself.
There are a few things you'll need to amend with your implementation to get it working.
1) The background circle, to look like the image you've posted, should have a grey strokeColor within your drawBgShape function:
bgShapeLayer.strokeColor = UIColor.lightGray.cgColor
2) The strokeEnd for timeLeftShapeLayer should be set to 0.0 as its starting value within your drawTimeLeftShape function.
timeLeftShapeLayer.strokeEnd = 0.0
3) Remove the strokeIt animation property from your class, you don't need it.
4) Update your click function to slowly increment the strokeEnd property of timeLeftShapeLayer directly. This is one simple line of code:
timeLeftShapeLayer.strokeEnd += 0.1
Here's an example implementation:
class ViewController: UIViewController {
let timeLeftShapeLayer = CAShapeLayer()
let bgShapeLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
drawBgShape()
drawTimeLeftShape()
}
func drawBgShape() {
bgShapeLayer.path = UIBezierPath(
arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY),
radius: 100,
startAngle: -90.degreesToRadians,
endAngle: 270.degreesToRadians,
clockwise: true
).cgPath
bgShapeLayer.strokeColor = UIColor.lightGray.cgColor
bgShapeLayer.fillColor = UIColor.clear.cgColor
bgShapeLayer.lineWidth = 15
view.layer.addSublayer(bgShapeLayer)
}
func drawTimeLeftShape() {
timeLeftShapeLayer.path = UIBezierPath(
arcCenter: CGPoint(x: view.frame.midX , y: view.frame.midY),
radius: 100,
startAngle: -90.degreesToRadians,
endAngle: 270.degreesToRadians,
clockwise: true
).cgPath
timeLeftShapeLayer.strokeColor = UIColor.red.cgColor
timeLeftShapeLayer.fillColor = UIColor.clear.cgColor
timeLeftShapeLayer.lineWidth = 15
timeLeftShapeLayer.strokeEnd = 0.0
view.layer.addSublayer(timeLeftShapeLayer)
}
#IBAction func click(_ sender: Any) {
timeLeftShapeLayer.strokeEnd += 0.1
}
}

I want to Rotate 3 different images on different location in 360 degree in swift 4 using UIBezierPath

I want to Rotate 3 different images on different location in 360 degree in swift 4 using UIBezierPath. i am able to move single image like this in image. with this code
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let circlePath = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX, y: view.frame.midY), radius: 120, startAngle: 0, endAngle:CGFloat(Double.pi)*2, clockwise: true)
let animation = CAKeyframeAnimation(keyPath: "position");
animation.duration = 5
animation.repeatCount = MAXFLOAT
animation.path = circlePath.cgPath
let moon = UIImageView()
moon.frame = CGRect(x:0, y:0, width:40, height:40);
moon.image = UIImage(named: "moon.png")
view.addSubview(moon)
moon.layer.add(animation, forKey: nil)
}
}
and I want to rotate 3 different images on different Angle and Position like this.i want to rotate all 3 different images like in this
Anyone here who can update my code according to the image above I want Thank You in Advance Cheers! :) .
I tried with your code and its working. I made a function to rotate a imageView.
func startMovingAView(startAnlge: CGFloat, endAngle: CGFloat) {
let circlePath = UIBezierPath(arcCenter: CGPoint(x: view.frame.midX, y: view.frame.midY), radius: 120, startAngle: startAnlge, endAngle:endAngle, clockwise: true)
let animation = CAKeyframeAnimation(keyPath: "position");
animation.duration = 5
animation.repeatCount = MAXFLOAT
animation.path = circlePath.cgPath
let moon = UIImageView()
moon.frame = CGRect(x:0, y:0, width:40, height:40);
moon.image = UIImage(named: "moon.png")
view.addSubview(moon)
moon.layer.add(animation, forKey: nil)
}
And here is the angles what I pass for each imageView:
/// For first imageView
var startAngle: CGFloat = 0.0
var endAngle: CGFloat = CGFloat(Double.pi) * 2.0
startMovingAView(startAnlge: startAngle, endAngle: endAngle)
/// For second imageView
startAngle = CGFloat(Double.pi/2.0)
endAngle = CGFloat(Double.pi) * 2.5
startMovingAView(startAnlge: startAngle, endAngle: endAngle)
/// For third imageView
startAngle = CGFloat(Double.pi)
endAngle = CGFloat(Double.pi) * 3.0
startMovingAView(startAnlge: startAngle, endAngle: endAngle)
We just need to find the angles with same difference so that in 5 seconds duration the imageView have to travel the same distance so all objects moves with constant and same speed.

Animate Custom UIView Property in Swift

all!
I have created a circular progress view using CoreGraphics that looks and updates like so:
50%
75%
The class is a UIView class, and it has a variable called 'progress' that determines how much of the circle is filled in.
It works well, but I want to be able to animate changes to the progress variable so that the bar animates smoothly.
I have read from myriad examples that I need to have a CALayer class along with the View class, which I have made, however, it doesn't animate at all.
Two questions:
Can I keep the graphic I drew in CoreGraphics, or do I need to somehow redraw it in CALayer?
My current (attempted) solution crashes towards the bottom at: anim.fromValue = pres.progress. What's up?
class CircleProgressView: UIView {
#IBInspectable var backFillColor: UIColor = UIColor.blueColor()
#IBInspectable var fillColor: UIColor = UIColor.greenColor()
#IBInspectable var strokeColor: UIColor = UIColor.greenColor()
dynamic var progress: CGFloat = 0.00 {
didSet {
self.layer.setValue(progress, forKey: "progress")
}
}
var distToDestination: CGFloat = 10.0
#IBInspectable var arcWidth: CGFloat = 20
#IBInspectable var outlineWidth: CGFloat = 5
override class func layerClass() -> AnyClass {
return CircleProgressLayer.self
}
override func drawRect(rect: CGRect) {
var fillColor = self.fillColor
if distToDestination < 3.0 {
fillColor = UIColor.greenColor()
} else {
fillColor = self.fillColor
}
//Drawing the inside of the container
//Drawing in the container
let center = CGPoint(x:bounds.width/2, y: bounds.height/2)
let radius: CGFloat = max(bounds.width, bounds.height) - 10
let startAngle: CGFloat = 3 * π / 2
let endAngle: CGFloat = 3 * π / 2 + 2 * π
let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.lineWidth = arcWidth
backFillColor.setStroke()
path.stroke()
let fill = UIColor.blueColor().colorWithAlphaComponent(0.15)
fill.setFill()
path.fill()
//Drawing the fill path. Same process
let fillAngleLength = (π) * progress
let fillStartAngle = 3 * π / 2 - fillAngleLength
let fillEndAngle = 3 * π / 2 + fillAngleLength
let fillPath_fill = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: fillStartAngle, endAngle: fillEndAngle, clockwise: true)
fillPath_fill.lineWidth = arcWidth
fillColor.setStroke()
fillPath_fill.stroke()
//Drawing container outline on top
let outlinePath_outer = UIBezierPath(arcCenter: center, radius: radius / 2 - outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
let outlinePath_inner = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth + outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
outlinePath_outer.lineWidth = outlineWidth
outlinePath_inner.lineWidth = outlineWidth
strokeColor.setStroke()
outlinePath_outer.stroke()
outlinePath_inner.stroke()
}
}
class CircleProgressLayer: CALayer {
#NSManaged var progress: CGFloat
override class func needsDisplayForKey(key: String) -> Bool {
if key == "progress" {
return true
}
return super.needsDisplayForKey(key)
}
override func actionForKey(key: String) -> CAAction? {
if (key == "progress") {
if let pres = self.presentationLayer() {
let anim: CABasicAnimation = CABasicAnimation.init(keyPath: key)
anim.fromValue = pres.progress
anim.duration = 0.2
return anim
}
return super.actionForKey(key)
} else {
return super.actionForKey(key)
}
}
}
Thanks for the help!
Try this out :)
class ViewController: UIViewController {
let progressView = CircleProgressView(frame:CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200))
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.frame = CGRectMake(0, 300, 200, 100)
button.backgroundColor = UIColor.yellowColor()
button.addTarget(self, action: #selector(ViewController.tap), forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(button)
view.addSubview(progressView)
progressView.progress = 1.0
}
func tap() {
if progressView.progress == 0.5 {
progressView.progress = 1.0
} else {
progressView.progress = 0.5
}
}
}
class CircleProgressView: UIView {
dynamic var progress: CGFloat = 0.00 {
didSet {
let animation = CABasicAnimation()
animation.keyPath = "progress"
animation.fromValue = circleLayer().progress
animation.toValue = progress
animation.duration = Double(0.5)
self.layer.addAnimation(animation, forKey: "progress")
circleLayer().progress = progress
}
}
func circleLayer() -> CircleProgressLayer {
return self.layer as! CircleProgressLayer
}
override class func layerClass() -> AnyClass {
return CircleProgressLayer.self
}
}
class CircleProgressLayer: CALayer {
#NSManaged var progress: CGFloat
override class func needsDisplayForKey(key: String) -> Bool {
if key == "progress" {
return true
}
return super.needsDisplayForKey(key)
}
var backFillColor: UIColor = UIColor.blueColor()
var fillColor: UIColor = UIColor.greenColor()
var strokeColor: UIColor = UIColor.greenColor()
var distToDestination: CGFloat = 10.0
var arcWidth: CGFloat = 20
var outlineWidth: CGFloat = 5
override func drawInContext(ctx: CGContext) {
super.drawInContext(ctx)
UIGraphicsPushContext(ctx)
//Drawing in the container
let center = CGPoint(x:bounds.width/2, y: bounds.height/2)
let radius: CGFloat = max(bounds.width, bounds.height) - 10
let startAngle: CGFloat = 3 * CGFloat(M_PI) / 2
let endAngle: CGFloat = 3 * CGFloat(M_PI) / 2 + 2 * CGFloat(M_PI)
let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.lineWidth = arcWidth
backFillColor.setStroke()
path.stroke()
let fill = UIColor.blueColor().colorWithAlphaComponent(0.15)
fill.setFill()
path.fill()
//Drawing the fill path. Same process
let fillAngleLength = (CGFloat(M_PI)) * progress
let fillStartAngle = 3 * CGFloat(M_PI) / 2 - fillAngleLength
let fillEndAngle = 3 * CGFloat(M_PI) / 2 + fillAngleLength
let fillPath_fill = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: fillStartAngle, endAngle: fillEndAngle, clockwise: true)
fillPath_fill.lineWidth = arcWidth
fillColor.setStroke()
fillPath_fill.stroke()
//Drawing container outline on top
let outlinePath_outer = UIBezierPath(arcCenter: center, radius: radius / 2 - outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
let outlinePath_inner = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth + outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
outlinePath_outer.lineWidth = outlineWidth
outlinePath_inner.lineWidth = outlineWidth
strokeColor.setStroke()
outlinePath_outer.stroke()
outlinePath_inner.stroke()
UIGraphicsPopContext()
}
}
Whilst AntonTheDev provides a great answer, his solution does not allow you to animate the CircularProgressView in an animation block, so you cant do neat things like:
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut,
animations: {
circularProgress.progress = 0.76
}, completion: nil)
There's a similar question here with a up to date Swift 3 answer based on ideas from the accepted answer and this post.
This is what the final solution looks like.
Swift 3 Solution

How are these animations typically done in Swift / iOS - See image

What do I need to learn in order to create similar animations to the ones shown in the following picture?
Can some one list all of the technologies involved and possibly a quick process as to how this is done?
For First animation you can use CAShapeLayer and CABasic Animation and animate key path strokeEnd
I builded exactly same you can have look at this link,download and see option Fill circle animation https://github.com/ajaykumar21091/AwesomeCustomAnimations-iOS/tree/master/SimpleCustomAnimations
Edit -
The basic idea here is to draw circle using bezeir path and animate the shapeLayer using CABasicAnimation using keyPath strokeEnd.
override func drawRect(rect: CGRect) {
self.drawBezierWithStrokeColor(circleColor.CGColor,
startAngle: 0,
endAngle: 360,
animated: false)
self.drawBezierWithStrokeColor(self.fillColor.CGColor,
startAngle: 0,
endAngle: (((2*CGFloat(M_PI))/100) * CGFloat(percentage)),
animated: true)
}
//helper methods.
private func drawBezierWithStrokeColor(color:CGColor, startAngle:CGFloat, endAngle:CGFloat, animated:Bool) {
let bezier:CAShapeLayer = CAShapeLayer()
bezier.path = bezierPathWithStartAngle(startAngle, endAngle: endAngle).CGPath
bezier.strokeColor = color
bezier.fillColor = UIColor.clearColor().CGColor
bezier.lineWidth = bounds.width * 0.18
self.layer.addSublayer(bezier)
if (animated) {
let animatedStrokeEnd:CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd");
animatedStrokeEnd.duration = (2.0/100)*Double(percentage);
animatedStrokeEnd.fromValue = NSNumber(float: 0.0);
animatedStrokeEnd.toValue = NSNumber(float: 1.0);
animatedStrokeEnd.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
bezier.addAnimation(animatedStrokeEnd, forKey: "strokeEndAnimation");
}
}
private func bezierPathWithStartAngle(startAngle:CGFloat, endAngle:CGFloat) -> UIBezierPath {
let center = CGPoint(x:bounds.width/2, y: bounds.height/2)
let radius = max(bounds.width, bounds.height)
let arcWidth = bounds.width * 0.25
let path = UIBezierPath(arcCenter : center,
radius : radius/2 - arcWidth/2,
startAngle : startAngle,
endAngle : endAngle ,
clockwise : true)
return path
}

Resources