eeschimosu eeschimosu - 5 days ago 5
iOS Question

How to create graph pies with different sizes using bézier paths in swift?

I want to make a nice graphic pie with 8 equal slices, that can be individually scaled or resized depending on an Int or something like this. This would look something like below just that all the slices should be equally cut:

enter image description here

I have tried this in Objective-C but it makes just one slice:

-(CAShapeLayer *)createPieSlice {
CAShapeLayer *slice = [CAShapeLayer layer];
slice.fillColor = [UIColor redColor].CGColor;
slice.strokeColor = [UIColor blackColor].CGColor;
slice.lineWidth = 3.0;

CGFloat angle = DEG2RAD(-60.0);
CGPoint center = CGPointMake(100.0, 100.0);
CGFloat radius = 100.0;

UIBezierPath *piePath = [UIBezierPath bezierPath];
[piePath moveToPoint:center];

[piePath addLineToPoint:CGPointMake(center.x + radius * cosf(angle), center.y + radius * sinf(angle))];

[piePath addArcWithCenter:center radius:radius startAngle:angle endAngle:DEG2RAD(60.0) clockwise:YES];

// [piePath addLineToPoint:center];
[piePath closePath]; // this will automatically add a straight line to the center
slice.path = piePath.CGPath;

return slice;
}


How can I achieve that graph in swift?

Answer

Break the problem into logical pieces.

You have wedges of different arc widths. All those radii need to add up to a full circle. I assume they represent fractions of something that adds up to 100%. Do you want a specific order? If so, map your fractions in the order you want, such that they all add up to 100%.

Then write code that starts at an angle of zero, and creates arcs that are the specified fraction of 2π. Each one would start at the end of the previous one. Assign a radius that's appropriate based on the data you need.

Now write code that creates closed path segments in a UIBezierPath.

EDIT

You've clarified, and told us that you always want 8 slices of the same width but with different radii.

So you need to write code that takes 8 input values and plots it as 8 arcs with different radius values.

Let's say your input value is an array of floats ranging from 0 to 1. At zero, the wedge is zero-sized. At 1.0, it's the largest circle size that will fit in your view (half the width of a square view.

So you would create an array of 8 floats:

var fractions = [0.5, 0.7, 0.3, 0.1, 1.0 .6, .2, .9]

The code to create a bezier curve with 8 arcs might look something like this:

let pi = 3.1415826
let largestRadius = myView.width/2

let piePath = UIBezierPath()
for (index, afloat) in fractions
{
  let startAngle = Double(index) / fractions.count * 2 * pi
  let endAngle = Double(index+1) / fractions.count * 2 * pi
  let thisRadius = largestRadius * afloat
  let center = CGPointMake( myView.width/2, myView.height/2)
  piePath.moveToPoint(center)

  piePath.addArcWithCenter(center,
    radius: thisRadius,
    startAngle: startAngle,
    endAngle: endAngle,
    clockwise: true)
  piePath.lineToPoint(center)
  piePath.closePath()
}

I think the code above would create 8 closed pie-slice paths, but I'm not positive. It might be necessary to add a lineToPoint call between the first moveToPoint call and the arc call.

Edit #2:

Since I am learning Swift, I decided to take this as an exercise and wrote a sample project that generates pie charts using a shape layer and a a custom path created from a UIBezierPath, as outlined above. You can find the sample project on github: PieCharts project on Github

Comments