I’ve a customized round play/pause button in UIKit that makes use of CAShapeLayer as a progress ring.
The whole lot works besides after I faucet the view, the border briefly flashes although strokeEnd = 0 and lineWidth = 0.
Right here is the total code for the customized view:
@IBDesignable
class CircularProgressView: UIView {
personal let progressLayer = CAShapeLayer()
personal let iconView = UIImageView()
@IBInspectable var ringWidth: CGFloat = 4
@IBInspectable var ringColor: UIColor = .white
@IBInspectable var playIcon: UIImage? = UIImage(named: "iconPause")
@IBInspectable var pauseIcon: UIImage? = UIImage(named: "iconPlay")
@IBInspectable var iconColor: UIColor = .white
@IBInspectable var isPlaying: Bool = false { didSet { updateIcon() } }
override init(body: CGRect) {
tremendous.init(body: body)
setup()
}
required init?(coder: NSCoder) {
tremendous.init(coder: coder)
setup()
}
personal func setup() {
backgroundColor = .clear
iconView.contentMode = .scaleAspectFit
iconView.tintColor = iconColor
addSubview(iconView)
// PROGRESS RING
layer.addSublayer(progressLayer)
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeColor = ringColor.cgColor
progressLayer.lineCap = .spherical
progressLayer.strokeEnd = 0
progressLayer.lineWidth = 0
addGestureRecognizer(UITapGestureRecognizer(goal: self, motion: #selector(viewTapped)))
updateIcon()
}
override func layoutSubviews() {
tremendous.layoutSubviews()
drawRing()
iconView.body = bounds.insetBy(dx: bounds.width * 0.28, dy: bounds.peak * 0.28)
}
personal func drawRing() {
let radius = min(bounds.width, bounds.peak) / 2 - ringWidth / 2
let middle = CGPoint(x: bounds.midX, y: bounds.midY)
let path = UIBezierPath(arcCenter: middle, radius: radius,
startAngle: -.pi/2, endAngle: 3 * .pi/2, clockwise: true)
progressLayer.body = bounds
progressLayer.path = path.cgPath
}
func setProgress(_ worth: CGFloat, animated: Bool = true) {
let clamped = max(0, min(worth, 1))
if animated {
let anim = CABasicAnimation(keyPath: "strokeEnd")
anim.fromValue = progressLayer.strokeEnd
anim.toValue = clamped
anim.period = 0.25
progressLayer.strokeEnd = clamped
progressLayer.add(anim, forKey: nil)
} else {
progressLayer.strokeEnd = clamped
}
}
personal func updateIcon() {
let picture = isPlaying ? pauseIcon : playIcon
iconView.picture = picture?.withRenderingMode(.alwaysTemplate)
iconView.tintColor = iconColor
}
@objc personal func viewTapped() {
isPlaying.toggle()
// <--- PROBLEM: TAP causes the ring/border to momentarily "flash"
}
}
❗Downside
Despite the fact that lineWidth = 0 and strokeEnd = 0, tapping the view nonetheless causes a short border flash on the finish of the circle. It seems like Core Animation attracts the stroke momentarily.
❓Query
How can I fully disable this flash and stop the border from rendering when tapping the view?
Is there an accurate method to disable implicit animations or reset the layer state so the stroke by no means seems?

