14.9 C
Canberra
Saturday, January 3, 2026

CAShapeLayer strokeEnd causes ring flash on faucet – find out how 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.
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?

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

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

[td_block_social_counter facebook="tagdiv" twitter="tagdivofficial" youtube="tagdiv" style="style8 td-social-boxed td-social-font-icons" tdc_css="eyJhbGwiOnsibWFyZ2luLWJvdHRvbSI6IjM4IiwiZGlzcGxheSI6IiJ9LCJwb3J0cmFpdCI6eyJtYXJnaW4tYm90dG9tIjoiMzAiLCJkaXNwbGF5IjoiIn0sInBvcnRyYWl0X21heF93aWR0aCI6MTAxOCwicG9ydHJhaXRfbWluX3dpZHRoIjo3Njh9" custom_title="Stay Connected" block_template_id="td_block_template_8" f_header_font_family="712" f_header_font_transform="uppercase" f_header_font_weight="500" f_header_font_size="17" border_color="#dd3333"]
- Advertisement -spot_img

Latest Articles