Update: Complete working code added
Here is some pseudocode to help you figure out where you tapped. This is with reference to the topmost figure. (I'm assuming your circle's center coincides with the view's center):
(1) Find the direction of the line-segment from the center of the circle to the touch point:
dx = touchPoint.x - circleCenter.x;
dy = touchPoint.y - circleCenter.y;
t = atan2(dy, dx); // some offsetting/direction adjustment might be required
(2) Figure out which of the octants the touch point lies in.
octant = floor(4 * t/M_PI); // will return a number between 0 and 7.
If your sectors are not evenly sized (but you know the anglular size of each sector) you can use an if-else sequence.
(3) The octants on the right hand side each have an "inner sector" and an annulus. If you want to test which of the two parts the touch took place in, you can first calculate the distance of the touch point from the center of the circle:
dist = sqrtf(dx * dx + dy * dy);
Obviously you'll need to know the inner radius of each octant, and then test
if ( dist < innerRadius[i]) ... // again, you might need to adjust the angle calculations to ensure that the right indices correspond to the right sector. See (working) code below...
innerRadius[8]
is an array containing the inner radius of each octant.
Here's some actual working code that programmatically generates a piechart and correctly detects the tap location. Simply replace the contents of ViewController.m
in a "Single View Application" template with this:
#import "ViewController.h"
static float innerRadii[] = {50, 75, 100, 125, 150, 175, 200, 225};
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
float side = 600; // square view
CGPoint center = CGPointMake(side/2, side/2);
CGFloat radius = side/2 * 0.9;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, side), YES, 0.0);
UIBezierPath *bgPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, side, side)];
[[UIColor lightGrayColor] setFill];
[bgPath fill];
for (int i = 0; i < 8; i++)
{
UIBezierPath *sector = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:M_PI/4 * i endAngle:M_PI/4 * (i+1) clockwise:YES];
[sector addLineToPoint:center];
[sector closePath];
#define VAL(x) x/2 + 0.25
[[UIColor colorWithRed:VAL((float)(i % 2)) green:VAL((float)((i >> 1) % 2)) blue:VAL((float)((i >> 2) % 2)) alpha:1.0] setFill];
[sector fill];
UIBezierPath *innerSector = [UIBezierPath bezierPathWithArcCenter:center radius:innerRadii[i] startAngle:M_PI/4 * i endAngle:M_PI/4 * (i+1) clockwise:YES];
[innerSector addLineToPoint:center];
[innerSector closePath];
#define VAL1(x) (1- x)/3 + 0.5
[[UIColor colorWithRed:VAL1((float)(i % 2)) green:VAL1((float)((i >> 1) % 2)) blue:VAL1((float)((i >> 2) % 2)) alpha:1.0] setFill];
[innerSector fill];
}
UIImage *pieChartImg = UIGraphicsGetImageFromCurrentImageContext();
UIImageView *pieChartView = [[UIImageView alloc] initWithImage:pieChartImg];
pieChartView.center = self.view.center;
[self.view addSubview:pieChartView];
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(identifyTappedSector:)];
pieChartView.userInteractionEnabled = YES;
[pieChartView addGestureRecognizer:gr];
}
- (void)identifyTappedSector:(UITapGestureRecognizer *)tgr
{
CGPoint touchPoint = [tgr locationInView:tgr.view];
CGPoint circleCenter = CGPointMake(tgr.view.bounds.size.width/2, tgr.view.bounds.size.height/2);
float dx = circleCenter.x - touchPoint.x;
float dy = circleCenter.y - touchPoint.y;
float t = atan2f(dy, dx) + M_PI;
NSLog(@"angle = %f", t * 180.0/M_PI);
int octant = floorf(4 * t/M_PI);
NSLog(@"You tapped octant number: %d!", octant);
float dist = sqrtf(dx * dx + dy * dy);
if (dist <= innerRadii[octant])
NSLog(@"\tYou tapped the inner sector!");
else
NSLog(@"\tYou tapped the annulus!");
}
@end