如何在iOS中创建旋转的彩虹色环

时间:2019-07-10 06:31:37

标签: ios swift xcode uibezierpath cgaffinetransform

从stackoverflow我得到了一个绘制彩虹色圆的代码。但是作为要求的一部分,我需要像旋转的进度加载程序那样连续旋转该圆。下面是用于创建彩虹色圆的代码。

    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 个答案:

答案 0 :(得分:1)

首先,您所获得的内容看起来过于复杂。看下面的例子:

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来旋转圆。

请注意,此代码将泄漏,因为在任何地方计时器都无效。当视图消失或类似现象时,需要将其删除。

答案 1 :(得分:0)

最简单的方法是附加一个永远重复的动画:

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,假设您将此代码添加到其中的一种方法中。

答案 2 :(得分:0)

为此,我们可以使用类似这样的图像

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()
    })
}