HomeiOS DevelopmentCAShapeLayer strokeEnd causes ring flash on faucet – the way to cease...

CAShapeLayer strokeEnd causes ring flash on faucet – the way to cease border flash on customized round progress button (UIKit)?


I’ve a customized round play/pause button in UIKit that makes use of CAShapeLayer as a progress ring.
Every little thing works besides after I faucet the view, the border briefly flashes regardless that strokeEnd = 0 and lineWidth = 0.

Right here is the total code for the customized view:

@IBDesignable
class CircularProgressView: UIView {
    
    non-public let progressLayer = CAShapeLayer()
    non-public 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()
    }

    non-public 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.top * 0.28)
    }

    non-public func drawRing() {
        let radius = min(bounds.width, bounds.top) / 2 - ringWidth / 2
        let heart = CGPoint(x: bounds.midX, y: bounds.midY)
        let path = UIBezierPath(arcCenter: heart, 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.length = 0.25
            progressLayer.strokeEnd = clamped
            progressLayer.add(anim, forKey: nil)
        } else {
            progressLayer.strokeEnd = clamped
        }
    }

    non-public func updateIcon() {
        let picture = isPlaying ? pauseIcon : playIcon
        iconView.picture = picture?.withRenderingMode(.alwaysTemplate)
        iconView.tintColor = iconColor
    }

    @objc non-public func viewTapped() {
        isPlaying.toggle()
        // <--- PROBLEM: TAP causes the ring/border to momentarily "flash"
    }
}

❗Downside

Regardless that lineWidth = 0 and strokeEnd = 0, tapping the view nonetheless causes a short border flash on the finish of the circle. It seems to be 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 solution to disable implicit animations or reset the layer state so the stroke by no means seems?

Picture : CAShapeLayer strokeEnd causes ring flash on faucet – the way to cease border flash on customized round progress button (UIKit)?

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments