Question

enter image description hereI want to find the area while tapping inside the circle. Actually I have done one calculation, but that was not accurate.

Check the code snippet I wrote, in order to find the position of the tab in my cycle view.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event" this method

     float  dx = touchLocation.x -160;
     float  dy = touchLocation.y - 240;
     double angle = atan2(touchLocation.x,touchLocation.y);
 /* Device is iPad */
            if (count==4) {


                if(angle>-1.639&&angle<=0.775)
                {
                    area=1;
                    NSLog(@"touched here  1 ********************************** ");
                }
                else if(angle>0.775&&angle<=1.579)
                {
                    area=2;
                    NSLog(@"touched here   2********************************** ");
                }
                else if(angle>1.579&&angle<=2.466)
                {
                    area=3;NSLog(@"touched here   3********************************** ");
                }
                else
                {
                    area=4;NSLog(@"touched here  4 ********************************** ");
                }


            }
            else  if (count==5) {


                if(angle>-1.520&&angle<=0.553)
                {
                    area=1;
                    NSLog(@"touched here  1 ********************************** ");
                }
                else if(angle>0.553&&angle<=1.262)
                {
                    area=2;
                    NSLog(@"touched here   2********************************** ");
                }
                else if(angle>1.262&&angle<=1.884)
                {
                    area=3;NSLog(@"touched here   3********************************** ");
                }
                else if(angle>1.884&&angle<=2.644)
                {
                    area=4;NSLog(@"touched here  4 ********************************** ");
                }

                else
                {
                    area=5;NSLog(@"touched here  5 ********************************** ");
                }


            }
            else  if (count==6) {


                if(angle>-1.5707&&angle<=0.4692)
                {
                    area=1;
                    NSLog(@"touched here  1 ********************************** ");
                }
                else if(angle>0.4692&&angle<=1.0219)
                {
                    area=2;
                    NSLog(@"touched here   2********************************** ");
                }
                else if(angle>1.0219&&angle<=1.5707)
                {
                    area=3;NSLog(@"touched here   3********************************** ");
                }
                else if(angle>1.5707&&angle<=2.1147)
                {
                    area=4;NSLog(@"touched here  4 ********************************** ");
                }
                else if(angle>2.1147&&angle<=2.7245)
                {
                    area=5;NSLog(@"touched here  5 ********************************** ");
                }
                else
                {
                    area=6;NSLog(@"touched here  6 ********************************** ");
                }


            }
            else  if (count==7) {


                if(angle>-1.5707&&angle<=0.3992)
                {
                    area=1;
                    NSLog(@"touched here  1 ********************************** ");
                }
                else if(angle>0.3992&&angle<=0.8602)
                {
                    area=2;
                    NSLog(@"touched here   2********************************** ");
                }
                else if(angle>0.8602&&angle<=1.346)
                {
                    area=3;NSLog(@"touched here   3********************************** ");
                }
                else if(angle>1.346&&angle<=1.812)
                {
                    area=4;NSLog(@"touched here  4 ********************************** ");
                }
                else if(angle>1.812&&angle<=2.304)
                {
                    area=5;NSLog(@"touched here  5 ********************************** ");
                }
                else if(angle>2.304&&angle<=2.828)
                {
                    area=6;NSLog(@"touched here  6 ********************************** ");
                }

                else
                {
                    area=7;NSLog(@"touched here  7 ********************************** ");
                }


            }
            else if (count==8){

                if(angle>-1.40&&angle<=0.45)
                {
                    area=1;
                    NSLog(@"touched here  1 ********************************** ");
                }
                else if(angle>.45&&angle<=.73)
                {
                    area=2;
                    NSLog(@"touched here   2********************************** ");
                }
                else if(angle>.73&&angle<=1.15)
                {
                    area=3;NSLog(@"touched here   3********************************** ");
                }
                else if(angle>1.15&&angle<=1.55){
                    area=4;NSLog(@"touched here  4 ********************************** ");
                }
                else if(angle>-1.55&&angle<=1.95){
                    area=5;NSLog(@"touched here  5 ********************************** ");
                }
                else if(angle>-1.95&&angle<=2.43){
                    area=6;NSLog(@"touched here  6 ********************************** ");
                }
                else if(angle>2.43&&angle<=2.98){
                    area=7;NSLog(@"touched here   7********************************** ");
                }

                //     else if(angle>2.98&&angle<=-1.40){
                else
                {
                    area=8;NSLog(@"touched here  8 ********************************** ");
                }

            } 

enter image description here

Was it helpful?

Solution

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

OTHER TIPS

Here are some thoughts, Kevin's answer is good but doesn't generalize for more slices.

Let's say you have n equal slices on a circle. Each slice 2*PI/n degrees. To find the corresponding slice, you have to get some help from trigonometry, in particular we will work with arctangents.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:cycleView];

double radius = self.bounds.size.width;
double sliceAngle = M_PI*2 / n;

double deltaAngle = atan2(radius - touchLocation.y,touchLocation.x - radius);
//Here you might get a negative value for delta angle, 
//just keep adding 2*M_PI to the result until you get a deltaAngle between 0 and 2*M_PI
//also notice that we are normalizing the aran values for the center of our frame, which is
//the origin of the circle (keep in mind that origin is the upper left corner in UIKit)

NSInteger sliceNumber = ((NSInteger)floor(deltaAngle/sliceAngle)) 
}

Also there is one extra thing you can do, since you have a circle, but UIView's should have rectangular shapes, you can override pointInside method to only capture touches inside the circle. It would look like this:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
//Assuming you have a circle, not an ellipse. And also that your frame is a square.

//Get the length of the touch point from the origin and make sure it's smaller than
//your circles radius

CGFloat length = sqrtf(point.x * point.x + point.y * point.y);
return (length < self.bounds.size.width); 
//could have used height as well since you have a square
}

These snippets were not tested, let me know if they don't work as intended.

Let's assume your cycle UIView is cycleView. Then this could be your solution:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:cycleView];

    if(touchLocation.y < cycleView.frame.size.height/2 && touchLocation.x < cycleView.frame.size.width/2){
        //touch is in upper left corner
    } else if(touchLocation.y < cycleView.frame.size.height/2 && touchLocation.x >= cycleView.frame.size.width/2){
        //touch is in upper right corner
    } else if(touchLocation.y >= cycleView.frame.size.height/2 && touchLocation.x < cycleView.frame.size.width/2){
        //touch is in lower left corner
    } else if(touchLocation.y >= cycleView.frame.size.height/2 && touchLocation.x >= cycleView.frame.size.width/2){
        //touch is in upper left corner
    }
}

it would be easier, if each pie piece would be a single UIView, like this you could call each separate.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top