Reputation: 176
I am trying to create 2 layers that stack on top of each other and are inside of a custom view class.
the drawing for the layers is working great but I just can't get them to adjust their location based on the location of the view which has been set using auto layouts where the view is created in code (no IB).
I have tried changing the frame of the layer with a call to layoutSubviews
here is the code for the try.
import UIKit
class ProgressView: UIView {
let displayLayer: CAShapeLayer = {
let display = CAShapeLayer()
display.strokeColor = UIColor.red.cgColor
display.lineWidth = 8.0
display.lineCap = CAShapeLayerLineCap.round
display.fillColor = UIColor.clear.cgColor
display.strokeEnd = 0.5
return display
}()
let trackLayer: CAShapeLayer = {
let track = CAShapeLayer()
track.strokeColor = UIColor.lightGray.cgColor
track.lineWidth = 6.0
track.lineCap = CAShapeLayerLineCap.round
track.fillColor = UIColor.clear.cgColor
return track
}()
override init(frame: CGRect) {
super.init(frame: frame)
let progressCircle = UIBezierPath(arcCenter: .zero, radius: 50, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
displayLayer.path = progressCircle.cgPath
trackLayer.path = progressCircle.cgPath
layer.addSublayer(trackLayer)
layer.addSublayer(displayLayer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
displayLayer.frame = bounds
trackLayer.frame = bounds
}
}
After some research I also tried the KVO approach and used the following code:
import UIKit
class ProgressView: UIView {
let displayLayer: CAShapeLayer = {
let display = CAShapeLayer()
display.strokeColor = UIColor.red.cgColor
display.lineWidth = 8.0
display.lineCap = CAShapeLayerLineCap.round
display.fillColor = UIColor.clear.cgColor
display.strokeEnd = 0.5
return display
}()
let trackLayer: CAShapeLayer = {
let track = CAShapeLayer()
track.strokeColor = UIColor.lightGray.cgColor
track.lineWidth = 6.0
track.lineCap = CAShapeLayerLineCap.round
track.fillColor = UIColor.clear.cgColor
return track
}()
override init(frame: CGRect) {
super.init(frame: frame)
let progressCircle = UIBezierPath(arcCenter: .zero, radius: 50, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
displayLayer.path = progressCircle.cgPath
trackLayer.path = progressCircle.cgPath
layer.addSublayer(trackLayer)
layer.addSublayer(displayLayer)
self.addObserver(self, forKeyPath: #keyPath(ProgressView.bounds), options: .new, context: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == #keyPath(ProgressView.bounds) {
displayLayer.frame = self.bounds
trackLayer.frame = self.bounds
}
}
}
With each example I keep getting the same result with the following being the display. Mind that purple box is the ProgressView
where I am expecting the two layers to be inside of.
I keep looking at this and thinking there is something simple that I am missing or doing wrong and I just can't see it.
I have put print statement in both observeValue
as well as layoutSubviews
to make sure they were being called and that was working.
Thanks in advance.
Upvotes: 3
Views: 811
Reputation: 15238
You need to provide the correct arcCenter
as below and no need to set a frame
for the shapLayer
.
override func layoutSubviews() {
super.layoutSubviews()
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let progressCircle = UIBezierPath(arcCenter: center, radius: 50, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
displayLayer.path = progressCircle.cgPath
trackLayer.path = progressCircle.cgPath
}
Result
Note: I used the first ProgressView
class without KVO.
Upvotes: 2