Positioning of CAShapeLayer - ios

I'm currently trying to implement a CAShapeLayer. It works fine, but its position isn't working the way I want it to.
I want the circle to be displayed inside of the top selected UIView you see in the second image below. To do that, I tried to set arcCenter to circleView.center. But what's being displayed is the image below.
How it's being displayed:
I want it to be displayed in the center inside of this UIView:
My code:
func displayCircle() {
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1)
let trackLayer = CAShapeLayer()
let center = circleView.center
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.groupTableViewBackground.cgColor
trackLayer.lineWidth = 20
trackLayer.fillColor = UIColor.clear.cgColor
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = 20
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = CAShapeLayerLineCap.round
shapeLayer.strokeEnd = 0
circleView.layer.addSublayer(trackLayer)
circleView.layer.addSublayer(shapeLayer)
}
Edit, UIView's constraints:

You can simply change your center position of circle view like below,
let center = CGPoint(x: circleView.frame.size.width/2, y: circleView.frame.size.height/2)
Hope this way may help you.

Related

CAShapeLayer is added to the view, but not around my button

I'd like to add a circular CAShapeLayer around my button that I've implemented with Storyboard. I'm doing this because I want to add a clockwise circle and pulsing animation similar to these two videos: #1 and #2. I don't want a border as a shape is required. I've tried two approaches to and it is not going directly around my button.
I've tried it with viewDidLoad and viewDidLayoutSubviews. Both approaches put it in the top left corner. For brevity's sake, I only attached the viewDidLayoutSubviews below. The only change in viewDidLoad is the addition of self.view.layoutIfNeeded().
var wasCAShapeLayerAddedToButton = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !wasCAShapeLayerAddedToButton {
wasCAShapeLayerAddedToButton = true
startBiteButton.layer.cornerRadius = startBiteButton.frame.width / 2
addCAShapeLayerToButton()
}
}
func addCAShapeLayerToButton() {
let circularPath = UIBezierPath(arcCenter: startBiteButton.center, radius: startBiteButton.frame.width / 2, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
let trackLayer = CAShapeLayer()
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineWidth = 2
trackLayer.lineCap = CAShapeLayerLineCap.round
view.layer.addSublayer(trackLayer)
recordLine.path = circularPath.cgPath
recordLine.strokeColor = UIColor(red: 0.42, green: 0.39, blue: 1.00, alpha: 1.00).cgColor
recordLine.fillColor = UIColor.clear.cgColor
recordLine.lineWidth = 2
recordLine.lineCap = CAShapeLayerLineCap.round
recordLine.strokeEnd = 0
view.layer.addSublayer(recordLine)
pulsatingLayer = CAShapeLayer()
pulsatingLayer.path = circularPath.cgPath
pulsatingLayer.strokeColor = UIColor.clear.cgColor
pulsatingLayer.fillColor = UIColor.yellow.cgColor
pulsatingLayer.lineWidth = 2
pulsatingLayer.lineCap = CAShapeLayerLineCap.round
view.layer.addSublayer(pulsatingLayer)
animatePulsatingLayer()
}
Here is how it's adding the shape.

How to add multiple circle on circular progress bar?

I have implemented circular progress bar. but now i want to add multiple circles on progress bar. like below image
I have implemented below code
let rect = rectForShape()
let circlePath = UIBezierPath(arcCenter: CGPoint(x:rect.midX , y: rect.midY), radius: CGFloat(10), startAngle: CGFloat(50), endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
// Change the fill color
shapeLayer.fillColor = UIColor.clear.cgColor
// You can change the stroke color
shapeLayer.strokeColor = UIColor.red.cgColor
// You can change the line width
shapeLayer.lineWidth = 3.0
view.layer.addSublayer(shapeLayer)

How draw a circular progress bar with CAShapLayer and auto layout constraints

I'm drawing a circular progress bar using CAShapeLayer, I can produce it only if I center it on my view. Now I want to constraint it to the trailing edge of my card view. Below is my code:
let center = cardView.center
//track layer
let trackLayer = CAShapeLayer()
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.gray.cgColor
trackLayer.strokeEnd = 1
trackLayer.lineCap = kCALineCapRound
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineWidth = 5
cardView.layer.addSublayer(trackLayer)
//progress Layer
let shapeLayer = CAShapeLayer()
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.yellow.cgColor
//NumberFormatter().number(from: percentageUsed) as! CGFloat
shapeLayer.strokeEnd = 0.6
shapeLayer.lineCap = kCALineCapRound
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 5
cardView.layer.addSublayer(shapeLayer)
Now I wish i could do something like
shapeLayer.leadingAnchor.constraint(equalTo: cardVeiw.leadingAnchor, constant: 20).isActive = true

Custom circular progress view in awakeFromNib

I have created circle progress view using UIBezierPath and CAShapeLayer with following code.
let center = self.center
let trackLayer = CAShapeLayer()
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = kCALineCapRound
self.layer.addSublayer(trackLayer)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 10
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = kCALineCapRound
shapeLayer.strokeEnd = 0
self.layer.addSublayer(shapeLayer)
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
If I put that code in my viewDidLoad it works fine. However, when I add UIView from storyboard and set its class to custom class then copy all codes to that custom UIView class and call it in awakeFromNib function it is not working what could be the reason? And how can I use above code with custom UIView class?
Orange view should be my custom view I have added leading, trailing, bottom and fixed height constraint it but it looks like above. Does not circle view need to place center of orange view?
Actually it works but it has wrong position. The position in this case is out of your view so you can't see it. It makes you think that your code doesn't work.
If you want to have circle in the center of your view. Put the code in awakeFromNib and calculate center your self, don't use self.center
override func awakeFromNib() {
let center = CGPoint.init(x: frame.width / 2, y: frame.height / 2)
let trackLayer = CAShapeLayer()
let shapeLayer = CAShapeLayer()
let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = kCALineCapRound
self.layer.addSublayer(trackLayer)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 10
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineCap = kCALineCapRound
shapeLayer.strokeEnd = 0
self.layer.addSublayer(shapeLayer)
}
Result

How to add gradient color to a CAShapeLayer created using UIBezierPath

I have tried this and this but still no success. Basically I want to create something like this (inner circle). Circle will be created according to some data lets say if data is 50 we will get a half circle and if its 100 we will get full circle.
And this is what I have so far, so how can I create the above design
I have created a View using interface builder, and drawn these circle using this code
override func viewDidLoad() {
let circlePath = UIBezierPath(arcCenter: CGPoint(x: myView.layer.frame.height/2,y: myView.layer.frame.height/2), radius: CGFloat(100), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.CGPath
shapeLayer.fillColor = UIColor.clearColor().CGColor
shapeLayer.strokeColor = UIColor.grayColor().CGColor
shapeLayer.lineWidth = 3.0
let colorCirclePath = UIBezierPath(arcCenter: CGPoint(x: myView.layer.frame.height/2,y: myView.layer.frame.height/2), radius: CGFloat(100), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let coloredShapeLayer = CAShapeLayer()
coloredShapeLayer.path = colorCirclePath.CGPath
coloredShapeLayer.fillColor = UIColor.clearColor().CGColor
coloredShapeLayer.strokeColor = UIColor.whiteColor().CGColor
coloredShapeLayer.lineWidth = 10.0
self.myView.layer.addSublayer(coloredShapeLayer)
self.myView.layer.addSublayer(shapeLayer)
}
This is how I am creating the gradient.
let gradient: CAGradientLayer = CAGradientLayer()
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).CGColor
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).CGColor
gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
self.myView.layer.insertSublayer(gradient, atIndex:0)
If you want a real simulation of gradient color you can check this SO answer .It's based on a cross that divided your rectangular view in 4 portions, then shift the colors on each portion to obtain a regular gradient applied to the layer mask.

Resources