How to fill a circle color by percentage value?

2020-07-17 06:50发布

问题:

In my application I have a requirement in such a way that I need to fill circle with red color with percentage values like 50%, 80% which I am getting from API like below image.

Currently I am using the below code to achieve this.

    let roundView = UIView(frame:CGRect(x: 100, y: 100, width: 250, height: 250))
    roundView.backgroundColor = UIColor.white
    roundView.layer.cornerRadius = roundView.frame.size.width / 2


    // bezier path
    let circlePath = UIBezierPath(arcCenter: CGPoint (x: roundView.frame.size.width / 2, y: roundView.frame.size.height / 2),
                                  radius: roundView.frame.size.width / 2,
                                  startAngle:CGFloat(M_PI_2),
                                  endAngle: CGFloat (M_PI * 2.0),
                                  clockwise: true)
    circlePath.move(to: roundView.center)
    // circle shape
    let circleShape = CAShapeLayer()
    circleShape.path = circlePath.cgPath
    circleShape.strokeColor = UIColor.black.cgColor
    circleShape.fillColor = UIColor.green.cgColor
    circleShape.lineWidth = 1.5        
    // add sublayer
    roundView.layer.addSublayer(circleShape)
    // add subview
    self.view.addSubview(roundView)*

it is showing like below

Can you please suggest me how to do this. Currently I am facing problem converting percentage to degrees.

回答1:

Your problem is that you are not drawing the full path - an arc alone will not do it. You need to start the path at the centre, and draw the straight edges of the segment as well as the arc. Here it is in Playground - I have tried to keep as much as possible to your style, but have introduced the parameters proportion which is your percentage, and startAngle which is the orientation of the segment.

import UIKit
let roundView = UIView(frame:CGRect(x: 100, y: 100, width: 250, height: 250))
roundView.backgroundColor = UIColor.white
roundView.layer.cornerRadius = roundView.frame.size.width / 2

// vary this to move the start of the arc
let startAngle = -CGFloat.pi / 2  // This corresponds to 12 0'clock
// vary this to vary the size of the segment, in per cent
let proportion = CGFloat(80)
let centre = CGPoint (x: roundView.frame.size.width / 2, y: roundView.frame.size.height / 2)
let radius = roundView.frame.size.width / 2
let arc = CGFloat.pi * 2 * proportion / 100 // i.e. the proportion of a full circle

// Start a mutable path
let cPath = UIBezierPath()
// Move to the centre
cPath.move(to: centre)
// Draw a line to the circumference
cPath.addLine(to: CGPoint(x: centre.x + radius * cos(startAngle), y: centre.y + radius * sin(startAngle)))
// NOW draw the arc
cPath.addArc(withCenter: centre, radius: radius, startAngle: startAngle, endAngle: arc + startAngle, clockwise: true)
// Line back to the centre, where we started (or the stroke doesn't work, though the fill does)
cPath.addLine(to: CGPoint(x: centre.x, y: centre.y))
// n.b. as @MartinR points out `cPath.close()` does the same!

// circle shape
let circleShape = CAShapeLayer()
circleShape.path = cPath.cgPath
circleShape.strokeColor = UIColor.black.cgColor
circleShape.fillColor = UIColor.green.cgColor
circleShape.lineWidth = 1.5
// add sublayer
roundView.layer.addSublayer(circleShape)
roundView

Bonus - add a text label

The OP threw a couple of curved balls after my initial answer, including orienting the segment (implemented above) and labelling the segment with the percentage, which really should have been a separate question, however, it's not an unreasonable thing to want to do on a pie-chart, so here its is...

// Bonus - add text layer
// choose your font
let fontSize = CGFloat(20)
let font = UIFont.systemFont(ofSize: fontSize)
let attributes = [NSFontAttributeName: font]
// Format the string
let str = String(format: "%3.0f%%", proportion)
// Calculate the text size
let textSize = str.size(attributes: attributes)

// Assume the centre of the text is half way along the bisector of the segment
let halfAngle = startAngle + arc / 2
let centreText = CGPoint(x: centre.x + radius * cos(halfAngle) / 2, y: centre.y + radius * sin(halfAngle) / 2)
// calculate the the lower left of the label given the size
let originText = CGPoint(x: centreText.x - textSize.width / 2, y: centreText.y - textSize.height / 2)
// Allocate the text layer
let label = CATextLayer()
label.font = font
label.fontSize = fontSize
label.frame = CGRect(origin: originText, size: textSize)
label.string = str
label.alignmentMode = kCAAlignmentCenter
label.foregroundColor = UIColor.black.cgColor
roundView.layer.addSublayer(label)
roundView