核心动画中的简单饼图

问题描述 投票:0回答:1

我试图在我的应用程序中使用Core Animation加入一个简单的饼图.我在网上找到了一篇文章来复制和调整,这似乎接近我所需要的。

https:/github.comtomnodapiechart_ios。

代码中提到了Nib文件(我不太明白),但我可以用编程方式来代替吗?我想这是需要修改的一行代码,也许我还需要添加一些其他编码:-。

     required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
         let view: UIView = Bundle.main.loadNibNamed("PieChartView", owner: self, options: nil)!.first as! UIView
     addSubview(view)

let行指的是Nib文件,但我怎样才能让它改指我的View Controller呢?这显然会导致一系列未解决的标识符错误,因为这2个文件并没有像它们应该的那样连接起来。在视图控制器上,我有以下内容,以及一些其他网点:--------------------------------。

    @IBOutlet weak var  pieChartView: PieChartView!

由于我是Xcode的新手,希望有一个简单的解决方案来解决这个问题。

ios swift core-animation
1个回答
0
投票

"我想在我的应用程序中加入一个 简单的 饼图在我的应用中使用核心动画"

首先,去掉""二字 简单的 从这句话中可以看出,你是一个初学者,甚至不了解在笔尖(xib)中布局的元素与通过代码创建元素的区别。我不想说得像个混蛋,但如果你是个初学者,甚至不了解在nib(xib)中布局的元素与通过代码创建元素,你将有一条漫长的道路要走。

虽然你链接到的例子 "有效",但它有很多限制,并采取了一些相当奇怪的方法来完成任务。比如说

  • 它被限制在5个或更少的片段上
  • 段值之和必须等于1.0。
  • 它几乎没有什么错误检查的方式。

也就是说,它 可以 是你开始学习的好地方。

下面是同样的代码,修改后不需要xib文件。它可以像这样使用。

class ViewController: UIViewController {

    @IBOutlet var pieChartView: MyPieChartView!

    override func viewDidLoad() {
        super.viewDidLoad()

        pieChartView.slices = [
            Slice(percent: 0.4, color: UIColor.red),
            Slice(percent: 0.3, color: UIColor.blue),
            Slice(percent: 0.2, color: UIColor.purple),
            Slice(percent: 0.1, color: UIColor.green)
        ]
    }

    override func viewDidAppear(_ animated: Bool) {
        pieChartView.animateChart()
    }
}

这是 MyPieChartView.swift ...

最初的改动 PieChartView.swift 文件的顶部,之间。

// MARK: Changes start here
// MARK: Changes end here

额外的变化,以允许 "逆时针"... 寻找新的Bool变种的实例 drawClockwise

import UIKit

class MyPieChartView: UIView {

    static let ANIMATION_DURATION: CGFloat = 1.4

// MARK: Changes start here
    var canvasView: UIView!

    var label1: UILabel!
    var label2: UILabel!
    var label3: UILabel!
    var label4: UILabel!
    var label5: UILabel!

    var label1XConst: NSLayoutConstraint!
    var label2XConst: NSLayoutConstraint!
    var label3XConst: NSLayoutConstraint!
    var label4XConst: NSLayoutConstraint!
    var label5XConst: NSLayoutConstraint!

    var label1YConst: NSLayoutConstraint!
    var label2YConst: NSLayoutConstraint!
    var label3YConst: NSLayoutConstraint!
    var label4YConst: NSLayoutConstraint!
    var label5YConst: NSLayoutConstraint!

    var drawClockwise: Bool = true

    var slices: [Slice]?
    var sliceIndex: Int = 0
    var currentPercent: CGFloat = 0.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        if canvasView == nil {

            let container = UIView()
            addSubview(container)

            canvasView = UIView()
            container.addSubview(canvasView)

            canvasView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                canvasView.topAnchor.constraint(equalTo: container.topAnchor),
                canvasView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
                canvasView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
                canvasView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
            ])

            canvasView.backgroundColor = .yellow

            label1 = UILabel()
            label2 = UILabel()
            label3 = UILabel()
            label4 = UILabel()
            label5 = UILabel()

            [label1, label2, label3, label4, label5].forEach {
                guard let v = $0 else { fatalError("Bad Setup!") }
                v.translatesAutoresizingMaskIntoConstraints = false
                v.textColor = .white
                v.textAlignment = .center
                addSubview(v)
            }

            label1XConst = label1.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor)
            label1YConst = label1.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor)

            label2XConst = label2.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor)
            label2YConst = label2.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor)

            label3XConst = label3.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor)
            label3YConst = label3.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor)

            label4XConst = label4.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor)
            label4YConst = label4.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor)

            label5XConst = label5.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor)
            label5YConst = label5.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor)

            [label1XConst, label2XConst, label3XConst, label4XConst, label5XConst,
             label1YConst, label2YConst, label3YConst, label4YConst, label5YConst].forEach {
                $0?.isActive = true
            }
        }

    }

    override func layoutSubviews() {
        super.layoutSubviews()
        subviews[0].frame = bounds
    }

    // don't do this
    //override func draw(_ rect: CGRect) {
    //  subviews[0].frame = bounds
    //}
// MARK: Changes end here

    /// Get an animation duration for the passed slice.
    /// If slice share is 40%, for example, it returns 40% of total animation duration.
    ///
    /// - Parameter slice: Slice struct
    /// - Returns: Animation duration
    func getDuration(_ slice: Slice) -> CFTimeInterval {
        return CFTimeInterval(slice.percent / 1.0 * PieChartView.ANIMATION_DURATION)
    }

    /// Convert slice percent to radian.
    ///
    /// - Parameter percent: Slice percent (0.0 - 1.0).
    /// - Returns: Radian
    func percentToRadian(_ percent: CGFloat) -> CGFloat {
        //Because angle starts wtih X positive axis, add 270 degrees to rotate it to Y positive axis.
        var angle = 270 + percent * 360
        if angle >= 360 {
            angle -= 360
        }
        return angle * CGFloat.pi / 180.0
    }

    /// Add a slice CAShapeLayer to the canvas.
    ///
    /// - Parameter slice: Slice to be drawn.
    func addSlice(_ slice: Slice) {
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = getDuration(slice)
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        animation.delegate = self

        let canvasWidth = canvasView.frame.width
        let toPercent = currentPercent + (drawClockwise ? slice.percent : -slice.percent)
        let path = UIBezierPath(arcCenter: canvasView.center,
                                radius: canvasWidth * 3 / 8,
                                startAngle: percentToRadian(currentPercent),
                                endAngle: percentToRadian(toPercent),
                                clockwise: drawClockwise)

        let sliceLayer = CAShapeLayer()
        sliceLayer.path = path.cgPath
        sliceLayer.fillColor = nil
        sliceLayer.strokeColor = slice.color.cgColor
        sliceLayer.lineWidth = canvasWidth * 2 / 8
        sliceLayer.strokeEnd = 1
        sliceLayer.add(animation, forKey: animation.keyPath)

        canvasView.layer.addSublayer(sliceLayer)
    }

    /// Get label's center position based on from and to percentages.
    /// This is always relative to canvasView's center.
    ///
    /// - Parameters:
    ///   - fromPercent: End of previous slice.
    ///   - toPercent: End of current slice.
    /// - Returns: Center point for label.
    func getLabelCenter(_ fromPercent: CGFloat, _ toPercent: CGFloat) -> CGPoint {
        let radius = canvasView.frame.width * 3 / 8
        let labelAngle = percentToRadian((toPercent - fromPercent) / 2 + fromPercent)
        let path = UIBezierPath(arcCenter: canvasView.center,
                                radius: radius,
                                startAngle: labelAngle,
                                endAngle: labelAngle,
                                clockwise: drawClockwise)
        path.close()
        return path.currentPoint
    }

    /// Re-position and draw label such as "43%".
    ///
    /// - Parameter slice: Slice whose label is drawn.
    func addLabel(_ slice: Slice) {
        let center = canvasView.center
        let labelCenter = getLabelCenter(currentPercent, currentPercent + (drawClockwise ? slice.percent : -slice.percent))
        let xConst = [label1XConst, label2XConst, label3XConst, label4XConst, label5XConst][sliceIndex]
        let yConst = [label1YConst, label2YConst, label3YConst, label4YConst, label5YConst][sliceIndex]
        xConst?.constant = labelCenter.x - center.x
        yConst?.constant = labelCenter.y - center.y

        let label = [label1, label2, label3, label4, label5][sliceIndex]
        label?.isHidden = true
        label?.text = String(format: "%d%%", Int(slice.percent * 100))
    }

    /// Call this to start pie chart animation.
    func animateChart() {
        sliceIndex = 0
        currentPercent = 0.0
        canvasView.layer.sublayers = nil

        if slices != nil && slices!.count > 0 {
            let firstSlice = slices![0]
            addLabel(firstSlice)
            addSlice(firstSlice)
        }
    }
}

extension MyPieChartView: CAAnimationDelegate {
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if flag {
            currentPercent += (drawClockwise ? slices![sliceIndex].percent : -slices![sliceIndex].percent)
            sliceIndex += 1
            if sliceIndex < slices!.count {
                let nextSlice = slices![sliceIndex]
                addLabel(nextSlice)
                addSlice(nextSlice)
            } else {
                //After animation is done, display all labels. Can be animated.
                for label in [label1, label2, label3, label4, label5] {
                    label?.isHidden = false
                }
            }
        }
    }
}

例如:

class ViewController: UIViewController {

    @IBOutlet var pieChartView: MyPieChartView!
    @IBOutlet var antiPieChartView: MyPieChartView!

    override func viewDidLoad() {
        super.viewDidLoad()

        pieChartView.slices = [
            Slice(percent: 0.4, color: UIColor.red),
            Slice(percent: 0.3, color: UIColor.blue),
            Slice(percent: 0.2, color: UIColor.purple),
            Slice(percent: 0.1, color: UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0))
        ]

        antiPieChartView.slices = [
            Slice(percent: 0.4, color: UIColor.red),
            Slice(percent: 0.3, color: UIColor.blue),
            Slice(percent: 0.2, color: UIColor.purple),
            Slice(percent: 0.1, color: UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0))
        ]

        // draw this pie anti-clockwise
        antiPieChartView.drawClockwise = false
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        pieChartView.animateChart()
        antiPieChartView.animateChart()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.