iOS - 圆形视图的微调器

iOS - Spinner to a circular view

我正在尝试将微调器效果添加到圆形视图。

    func spin() {
        let circlePathLayer = CAShapeLayer()
        circlePathLayer.fillColor = nil
        circlePathLayer.strokeColor = UIColor.redColor.cgColor
        circlePathLayer.lineWidth = 6
        layer.addSublayer(circlePathLayer)
        setPath(to: circlePathLayer)
        animate(layer: circlePathLayer)
    }
    
    private func setPath(to layer: CAShapeLayer) {
        layer.path = UIBezierPath(ovalIn: bounds.insetBy(dx: layer.lineWidth / 2,
                                                                   dy: layer.lineWidth / 2)).cgPath
    }
    
    private func animate(layer: CAShapeLayer) {
        animateKeyPath(keyPath: "strokeEnd",
                       duration: 1,
                       values: [0, 1],
                       layer: layer)
        animateKeyPath(keyPath: "strokeStart",
                       duration: 1,
                       values: [(0 - 0.1) as CGFloat, (1 - 0.1) as CGFloat],
                       layer: layer)
    }
    
    private func animateKeyPath(keyPath: String, duration: CFTimeInterval, values: [CGFloat], layer: CAShapeLayer) {
        let animation = CAKeyframeAnimation(keyPath: keyPath)
        animation.values = values
        animation.calculationMode = .linear
        animation.duration = duration
        animation.repeatCount = Float.infinity
        layer.add(animation, forKey: animation.keyPath)
    }

但是对于上面的代码,在动画结束时duration,每次在strokeEnd1位置都会发生闪烁。如何克服闪烁并调节微调器?

为了直观地观察效果,​​我将持续时间设置为 5 秒。当微调器到达 3 点钟位置时请注意。

我认为 strokeStartstrokeEnd 被限制在 0 和 1 之间。当您尝试将 strokeStart 设置为负数(如 -0.1)时,系统会将其固定为零。

试试这个解决方案:

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class SpinnerView : UIView {
    let circlePathLayer = CAShapeLayer()

    override func didMoveToSuperview() {
        layer.addSublayer(circlePathLayer)
    }

    func spin() {
        let circleFrame = bounds.insetBy(dx: -40, dy: -40)
        circlePathLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
        
        circlePathLayer.strokeColor = UIColor.red.cgColor
        circlePathLayer.fillColor = nil
        circlePathLayer.lineWidth = 6

        setPath(to: circlePathLayer)
        animate(layer: circlePathLayer)
    }

    private func setPath(to layer: CAShapeLayer) {
        let path = UIBezierPath()
        path.addArc(withCenter: CGPoint(x: layer.bounds.midX,
                                        y: layer.bounds.midY),
                    radius: (bounds.width / 2.0) - layer.lineWidth / 2,
                    startAngle: 0,
                    endAngle: 2 * .pi / 10.0,
                    clockwise: true)
        layer.path = path.cgPath
    }

    private func animate(layer: CAShapeLayer) {
        let animation = CABasicAnimation(keyPath: "transform.rotation")
        animation.duration = 1
        animation.repeatCount = Float.infinity
        animation.fromValue = 0
        animation.toValue = 2 * CGFloat.pi
        layer.add(animation, forKey: "transform.rotation")
    }
}

class MyViewController : UIViewController {
    let spinner = SpinnerView()

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        spinner.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(spinner)

        view.addConstraints([
            NSLayoutConstraint(item: spinner,
                               attribute: .centerX,
                               relatedBy: .equal,
                               toItem: view,
                               attribute: .centerX,
                               multiplier: 1.0,
                               constant: 0),
            NSLayoutConstraint(item: spinner,
                               attribute: .centerY,
                               relatedBy: .equal,
                               toItem: view,
                               attribute: .centerY,
                               multiplier: 1.0,
                               constant: 0),
            NSLayoutConstraint(item: spinner,
                               attribute: .width,
                               relatedBy: .equal,
                               toItem: view,
                               attribute: .width,
                               multiplier: 1.0,
                               constant: -40)
        ])

        spinner.addConstraints([
            NSLayoutConstraint(item: spinner,
                               attribute: .width,
                               relatedBy: .equal,
                               toItem: spinner,
                               attribute: .height,
                               multiplier: 1.0,
                               constant: 0)
        ])

        self.view = view
    }

    override func viewDidLayoutSubviews() {
        spinner.spin()
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()