I have a pie chart which is made using UIBezierPath’s. I now need those individual paths (pie pieces) to be scalable. I believe you need a view to be able to use pinch scaling, so I think touchesMoved: is the way to go (unless there’s a workaround).
Any advice or help is appreciated!
Updated/Progress code
MySliceClass.m
+ (UIBezierPath *)sliceRadius:(float)radius andStartingAngle:(float)startingAngle andFinishingAngle:(float)finishingAngle
{
static UIBezierPath *path = nil;
path = [UIBezierPath bezierPath];
CGPoint center = {300,300};
[path moveToPoint:center];
[path addArcWithCenter:center radius:radius startAngle:radians(startingAngle) endAngle:radians(finishingAngle) clockwise:YES];
[path closePath];
path.lineWidth = 1;
[[UIColor redColor] setFill];
[path fill];
return path;
}
MySliceView.m
- (void)drawRect:(CGRect)rect
{
NSArray *arrayOfSlices = [NSArray arrayWithObjects:
slice01 = [WordplaySlice sliceRadius:200 andStartingAngle:0.5 andFinishingAngle:29.5],
slice02 = [WordplaySlice sliceRadius:200 andStartingAngle:30.5 andFinishingAngle:59.5],
slice03 = [WordplaySlice sliceRadius:200 andStartingAngle:60.5 andFinishingAngle:89.5],
slice04 = [WordplaySlice sliceRadius:200 andStartingAngle:90.5 andFinishingAngle:119.5],
slice05 = [WordplaySlice sliceRadius:200 andStartingAngle:120.5 andFinishingAngle:149.5],
slice06 = [WordplaySlice sliceRadius:200 andStartingAngle:150.5 andFinishingAngle:179.5],
slice07 = [WordplaySlice sliceRadius:200 andStartingAngle:180.5 andFinishingAngle:209.5],
slice08 = [WordplaySlice sliceRadius:200 andStartingAngle:210.5 andFinishingAngle:239.5],
slice09 = [WordplaySlice sliceRadius:200 andStartingAngle:240.5 andFinishingAngle:269.5],
slice10 = [WordplaySlice sliceRadius:200 andStartingAngle:270.5 andFinishingAngle:299.5],
slice11 = [WordplaySlice sliceRadius:200 andStartingAngle:300.5 andFinishingAngle:329.5],
slice12 = [WordplaySlice sliceRadius:200 andStartingAngle:330.5 andFinishingAngle:359.5], nil];
}
I think you will find it easier if you create a view for each slice, and use a
UIPinchGestureRecognizer. Here’s how.First, we need a
UIViewsubclass that draws one slice. It should also overridepointInside:withEvent:to ignore a touch that lands outside the slice (even if the touch is inside the view’s rectangular bounds).So we’ll make a class called
SliceView. It usesCAShapeLayerto do the slice drawing:We tell it to use a
CAShapeLayerinstead of a plainCALayerby overriding thelayerClassmethod. We’ll also add a handy method that returns the view’s layer as aCAShapeLayer.We’ll compute the path of the slice in
layoutSubviews, because the view receives thelayoutSubviewsmessage any time its size is changed.We’re going to lay out each slice view to cover the entire pie, but only draw its wedge of the pie. Each slice’s frame will cover the entire screen (if the pie is full-screen). That means the slice view knows that the center of its arc is at the center of its bounds. But then we use a little trigonometry to put in the padding between adjacent slices.
We also adjust the anchor point of the layer; this is the point in the layer that doesn’t move when you scale or rotate the layer. We want the anchor point to be at the corner of the slice nearest the center.
When any of the view’s properties relating to the slice are changed, we need to recompute the path outlining the slice. And when the fill color of the slice is changed, we need to pass that change along to the layer. So we’ll override the property setters.
Finally, we override
pointInside:withEvent:so that hit-testing will only assign a touch to a slice view if the touch is actually inside the path of the slice. This is critical since all of the slice views will have a frame that covers the whole screen.Now that we have a handy
SliceViewclass, we can use it to draw a pie chart with zoomable slices. It’s hard to fit two fingers into a slice on an iPhone screen, so we’ll let the user tap a slice to select it, and pinch anywhere to scale the selected slice. (This interface also makes it testable in the simulator.)We’ll draw unselected slices in red and the selected slice in blue.
When the user taps a slice, we’ll need to change the colors of the prior selection and the new selection, and record the new selection.
When the user pinches, we adjust the transform of the selected slice, if there is one.
Finally, we need to actually create the slice views and the gesture recognizers. We create one tap recognizer for each slice, and one “global” pinch recognizer attached to the background view.
And here’s what it looks like:
You can download my test project here: http://dl.dropbox.com/u/26919672/pie.zip
UPDATE
In response to your comment asking about limiting the scale, I would suggest adding some more properties to
SliceView:Important: You will need to initialize all three properties to 1 in
initWithFrame:andinitWithCoder:.Then, implement the
scalesetter to actually enforce the limits and set the scale:In
pinched:, you update thescaleproperty of the view instead of setting the view’stransformproperty directly: