Как создать вращающийся радужный цветовой круг в iOS - PullRequest
0 голосов
/ 10 июля 2019

Из stackoverflow я получил код для рисования радужного цветового круга. Но как часть требования мне нужно, чтобы этот круг вращался непрерывно, как вращающийся загрузчик прогресса. Ниже приведен код, используемый для создания цветового круга Rainbow.

    class RainbowCircle: UIView {

    private var radius: CGFloat {
        return frame.width>frame.height ? frame.height/2 : frame.width/2
    }

    private var stroke: CGFloat = 10
    private var padding: CGFloat = 5

    //MARK: - Drawing
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        drawRainbowCircle(outerRadius: radius - padding, innerRadius: radius - stroke - padding, resolution: 1)
    }

    init(frame: CGRect, lineHeight: CGFloat) {
        super.init(frame: frame)
        stroke = lineHeight
    }

    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }

    /*
     Resolution should be between 0.1 and 1
     */
    private func drawRainbowCircle(outerRadius: CGFloat, innerRadius: CGFloat, resolution: Float) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        context.saveGState()
        context.translateBy(x: self.bounds.midX, y: self.bounds.midY) //Move context to center

        let subdivisions:CGFloat = CGFloat(resolution * 512) //Max subdivisions of 512

        let innerHeight = (CGFloat.pi*innerRadius)/subdivisions //height of the inner wall for each segment
        let outterHeight = (CGFloat.pi*outerRadius)/subdivisions

        let segment = UIBezierPath()
        segment.move(to: CGPoint(x: innerRadius, y: -innerHeight/2))
        segment.addLine(to: CGPoint(x: innerRadius, y: innerHeight/2))
        segment.addLine(to: CGPoint(x: outerRadius, y: outterHeight/2))
        segment.addLine(to: CGPoint(x: outerRadius, y: -outterHeight/2))
        segment.close()

        //Draw each segment and rotate around the center
        for i in 0 ..< Int(ceil(subdivisions)) {
            UIColor(hue: CGFloat(i)/subdivisions, saturation: 1, brightness: 1, alpha: 1).set()
            segment.fill()
            //let lineTailSpace = CGFloat.pi*2*outerRadius/subdivisions  //The amount of space between the tails of each segment
            let lineTailSpace = CGFloat.pi*2*outerRadius/subdivisions
            segment.lineWidth = lineTailSpace //allows for seemless scaling
            segment.stroke()

//            //Rotate to correct location
            let rotate = CGAffineTransform(rotationAngle: -(CGFloat.pi*2/subdivisions)) //rotates each segment


            segment.apply(rotate)



        }

Пожалуйста, кто-нибудь поможет мне с вращением этого круга.

Пожалуйста, найдите под кружком, сгенерированным вышеуказанным кодом: enter image description here

Ответы [ 3 ]

1 голос
/ 10 июля 2019

То, что вы получили, в первую очередь выглядит слишком сложным. Взгляните на следующий пример:

class ViewController: UIViewController {

    class RainbowView: UIView {

        var segmentCount: Int = 10 {
            didSet {
                refresh()
            }
        }
        var lineWidth: CGFloat = 10 {
            didSet {
                refresh()
            }
        }

        override var frame: CGRect {
            didSet {
                refresh()
            }
        }

        override func layoutSubviews() {
            super.layoutSubviews()
            refresh()
        }

        private var currentGradientLayer: CAGradientLayer?

        private func refresh() {
            currentGradientLayer?.removeFromSuperlayer()

            guard segmentCount > 0 else { return }

            currentGradientLayer = {
                let gradientLayer = CAGradientLayer()
                gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
                gradientLayer.endPoint = CGPoint(x: 0.5, y: 0)
                gradientLayer.type = .conic
                let colors: [UIColor] = {
                    var colors: [UIColor] = [UIColor]()
                    for i in 0..<segmentCount {
                        colors.append(UIColor(hue: CGFloat(i)/CGFloat(segmentCount), saturation: 1, brightness: 1, alpha: 1))
                    }
                    colors.append(UIColor(hue: 0.0, saturation: 1, brightness: 1, alpha: 1)) // Append start color at the end as well to complete the circle
                    return colors;
                }()
                gradientLayer.colors = colors.map { $0.cgColor }

                gradientLayer.frame = bounds
                layer.addSublayer(gradientLayer)

                gradientLayer.mask = {
                    let shapeLayer = CAShapeLayer()
                    shapeLayer.frame = bounds
                    shapeLayer.lineWidth = lineWidth
                    shapeLayer.strokeColor = UIColor.white.cgColor
                    shapeLayer.fillColor = UIColor.clear.cgColor
                    shapeLayer.path = UIBezierPath(ovalIn: bounds.inset(by: UIEdgeInsets(top: lineWidth*0.5, left: lineWidth*0.5, bottom: lineWidth*0.5, right: lineWidth*0.5))).cgPath
                    return shapeLayer
                }()

                return gradientLayer
            }()


        }

    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview({
            let view = RainbowView(frame: CGRect(x: 50.0, y: 100.0, width: 100.0, height: 100.0))

            var angle: CGFloat = 0.0
            Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true, block: { _ in
                angle += 0.01
                view.transform = CGAffineTransform(rotationAngle: angle)
            })

            return view
        }())
    }

}

Таким образом, создается представление, которое использует конический градиент с маской для рисования круга, который вы описываете. Затем преобразование применяется к представлению, чтобы повернуть его. И Timer планируется вращать круг.

Обратите внимание, что этот код утечет, потому что таймер нигде не признан недействительным. Его нужно удалить, когда вид исчезнет или похожий.

0 голосов
/ 10 июля 2019

Для этого у нас может быть что-то вроде этого

Like this Image

syncImage.image = UIImage(named:"spinning")

Создайте расширение ниже для запуска / остановки вращения

extension UIView {
    // To animate
    func startRotating(duration: Double = 1) {
        let kAnimationKey = "rotation"

        if self.layer.animationForKey(kAnimationKey) == nil {
            let animate = CABasicAnimation(keyPath: "transform.rotation")
            animate.duration = duration
            animate.repeatCount = Float.infinity            
            animate.fromValue = 0.0
            animate.toValue = Float(M_PI * 2.0)
            self.layer.addAnimation(animate, forKey: kAnimationKey)
        }
    }
    func stopRotating() {
        let kAnimationKey = "rotation"

        if self.layer.animationForKey(kAnimationKey) != nil {
            self.layer.removeAnimationForKey(kAnimationKey)
        }
    }
}

Использование

func startSpinning() {
    syncImage.startRotating()
}

func stopSpinning() {
    syncImage.stopRotating()
}

func handleSyncTap(sender: UITapGestureRecognizer? = nil) {
    startSpinning()

    let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * Double(NSEC_PER_SEC)))

    dispatch_after(dispatchTime, dispatch_get_main_queue(), {
        self.stopSpinning()
    })
}
0 голосов
/ 10 июля 2019

Самый простой способ - добавить анимацию, которая повторяется навсегда:

let animation = CABasicAnimation(keyPath: "transform.rotation") // Create rotation animation
animation.repeatCount = .greatestFiniteMagnitude                // Repeat animation for as long as we can
animation.fromValue = 0                                         // Rotate from 0
animation.toValue = 2 * Float.pi                                //        to 360 deg
animation.duration = 1                                          // During 1 second

self.layer.add(animation, forKey: "animation")                  // Adding the animation to the view

self - это RainbowCircle, при условии, что вы добавите этот код к одному из методов внутри него.

...