首页 > 解决方案 > 如何在 iOS 中创建一个旋转的彩虹色圈

问题描述

从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)



        }

请任何人帮助我旋转这个圆圈。

请在上面代码生成的圆圈下方找到: 在此处输入图像描述

标签: iosswiftxcodeuibezierpathcgaffinetransform

解决方案


首先,您所得到的看起来完全过于复杂。看看下面的例子:

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

}

因此生成了一个视图,该视图使用带有蒙版的锥形渐变来绘制您所描述的圆圈。然后将变换应用于视图以旋转它。并且 aTimer预定旋转圆圈。

请注意,此代码将泄漏,因为计时器无处失效。当视图消失或类似情况时需要将其删除。


推荐阅读