2018년 8월 29일 수요일

파이 차트 (애니메이션 포함)





class CircularProgressView: UIView {
    
    enum Style {
        case donut
        case circle
    }
    
    private let kMaxProgress: Float = 1.0
    private var style = Style.donut
    private var trackTintColor: UIColor = UIColor.gray
    private var progressTintColor: UIColor = UIColor.orange
    private var lineWidth: Float = 0.0
    private var progress: Float = 0.0
    private var animation: Bool = false
    
    func drawDonutChart(progress: Float, lineWidth: Float = 5.0, trackColor: UIColor = UIColor.rgb(233, 239, 241), progressColor: UIColor = UIColor.rgb(5, 200, 123)) {
        self.style = .donut
        self.progress = progress
        self.lineWidth = lineWidth
        self.trackTintColor = trackColor
        self.progressTintColor = progressColor
        setNeedsDisplay()
    }
    
    func drawDonutChartWithAnimation(progress: Float, lineWidth: Float = 5.0, progressColor: UIColor = UIColor.rgb(5, 200, 123)) {
        self.animation = true
        self.layer.sublayers = nil
        let layer = CAShapeLayer()
        layer.frame = self.bounds
        layer.strokeColor = progressColor.cgColor
        layer.fillColor = UIColor.clear.cgColor
        layer.lineWidth = CGFloat(lineWidth)
        layer.path = bezierCgPath(progress: progress)
        layer.strokeEnd = 1
        
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = 0.5
        animation.fromValue = 0.0
        animation.toValue = 1.0
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        layer.add(animation, forKey: nil)
        
        self.layer.addSublayer(layer)
    }
    
    func drawPieChart(color: UIColor = UIColor.rgb(233, 239, 241)) {
        self.lineWidth = 0
        self.style = .circle
        self.trackTintColor = color
        setNeedsDisplay()
    }
    
    private func drawDonutProgress(progress: Float, center: CGPoint, radius: CGFloat, context: CGContext) {
        UIGraphicsPushContext(context)
        context.beginPath()
        let startAngle = -Float.pi / 2.0
        let endAngle = startAngle + (progress * 2.0 * Float.pi)
        context.addArc(center: center, radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: false)
        context.strokePath()
        UIGraphicsPopContext()
    }
    
    private func drawPieProgress(progress: Float, center: CGPoint, radius: CGFloat, context: CGContext) {
        UIGraphicsPushContext(context)
        context.beginPath()
        let startAngle = -Float.pi / 2.0
        let endAngle = startAngle + (progress * 2.0 * Float.pi)
        context.move(to: center)
        context.addArc(center: center, radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: false)
        context.closePath()
        context.fillPath()
        UIGraphicsPopContext()
    }
    
    private func bezierCgPath(progress: Float) -> CGPath {
        let bezierPath = UIBezierPath()
        bezierPath.addArc(withCenter: CGPoint(x: layer.frame.size.width / 2.0, y: layer.frame.size.height / 2.0),
                          radius: layer.frame.size.width / 2.0,
                          startAngle: CGFloat(-Float.pi / 2.0),
                          endAngle: CGFloat(-Float.pi / 2.0 + (progress * 2.0 * Float.pi)),
                          clockwise: true)
        return bezierPath.cgPath
    }
    
    override func draw(_ rect: CGRect) {
        if animation {
            return
        }
        var middlePoint = CGPoint()
        middlePoint.x = self.bounds.origin.x + self.bounds.size.width / 2.0
        middlePoint.y = self.bounds.origin.y + self.bounds.size.height / 2.0
        
        var radius = min(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0)
        radius -= (CGFloat(lineWidth) / 2.0)
        
        let context = UIGraphicsGetCurrentContext()
        context?.saveGState()
        context?.setLineWidth(CGFloat(lineWidth))
        
        if let context = context {
            switch style {
            case .donut:
                trackTintColor.setStroke()
                drawDonutProgress(progress: kMaxProgress, center: middlePoint, radius: radius, context: context)
                progressTintColor.setStroke()
                drawDonutProgress(progress: progress, center: middlePoint, radius: radius, context: context)
            case .circle:
                trackTintColor.setFill()
                drawPieProgress(progress: kMaxProgress, center: middlePoint, radius: radius, context: context)
            }
        }
        
        context?.restoreGState()
    }


}

댓글 없음:

댓글 쓰기