Reputation: 407
I am trying to position a circular progress bar but I am failing constantly. I guess I am not understanding the concept of frames and views and bounds. I found this post on stack overflow that shows me exactly how to construct a circular progress bar.
However, in the post, a circleView was created as UIView using CGRect(x: 100, y: 100, width: 100, height: 100)
In my case, manually setting the x and y coordinates is obv a big no. So the circleView has to be in the centre of it's parent view.
Here is my view hierarchy:
view -> view2ForHomeController -> middleView -> circleView
So everything is positioned using auto layout. Here is the problem:The circleView is adding properly and it positions itself at the x and y value I specify but how can I specify the x and y values of the center of middleView. I tried the following but the center values are returned as 0.0, 0.0.
self.view.addSubview(self.view2ForHomeController)
self.view2ForHomeController.fillSuperview()
let xPosition = self.view2ForHomeController.middleView.frame.midX
let yPostion = self.view2ForHomeController.middleView.frame.midY
print(xPosition) // ------- PRINTS ZERO
print(yPostion) // ------- PRINTS ZERO
let circle = UIView(frame: CGRect(x: xPosition, y: yPostion, width: 100, height: 100))
circle.backgroundColor = UIColor.green
self.view2ForHomeController.middleView.addSubview(circle)
var progressCircle = CAShapeLayer()
progressCircle.frame = self.view.bounds
let lineWidth: CGFloat = 10
let rectFofOval = CGRect(x: lineWidth / 2, y: lineWidth / 2, width: circle.bounds.width - lineWidth, height: circle.bounds.height - lineWidth)
let circlePath = UIBezierPath(ovalIn: rectFofOval)
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath.cgPath
progressCircle.strokeColor = UIColor.white.cgColor
progressCircle.fillColor = UIColor.clear.cgColor
progressCircle.lineWidth = 10.0
progressCircle.frame = self.view.bounds
progressCircle.lineCap = CAShapeLayerLineCap(rawValue: "round")
circle.layer.addSublayer(progressCircle)
circle.transform = circle.transform.rotated(by: CGFloat.pi/2)
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1.1
animation.duration = 1
animation.repeatCount = MAXFLOAT
animation.fillMode = CAMediaTimingFillMode.forwards
animation.isRemovedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
progressCircle.add(animation, forKey: nil)
Upvotes: 0
Views: 3326
Reputation: 407
The solution is to first add the subviews and then create the entire circular progress view at
override func viewDidLayoutSubviews(){
super.viewDidLayoutSubviews()
// Add and position the circular progress bar and its layers here
}
Upvotes: 0
Reputation: 535306
Frame is in terms of superview/superlayer coordinates. If you are going to say
circle.layer.addSublayer(progressCircle)
then you must give progressCircle
a frame
in terms of the bounds
of circle.layer
.
As for centering, in general, to center a sublayer in its superlayer you say:
theSublayer.position = CGPoint(
x:theSuperlayer.bounds.midX,
y:theSuperlayer.bounds.midY)
And to center a subview in its superview you say:
theSubview.center = CGPoint(
x:theSuperview.bounds.midX,
y:theSuperview.bounds.midY)
Upvotes: 3
Reputation: 16327
The bounds are the layer/view's coordinates in its local coordinate system. The frame is the layer/view's coordinates in its parents coordinate system. Since layers do not participate in auto layout, you should implement UIViewController.viewDidLayoutSubviews
(or UIView.layoutSubLayers
) and set the frame
of the layer to the bounds
of its super layer's view (the backing layer of a UIView essentially is the CoreGraphics version of that view).
This also means you need to recompute your path in they layout method. if that is expensive then draw your path in unit space and use the transform of the layer to scale it up to the view's dimensions instead.
Upvotes: 1