1

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

rmaddy
  • 314,917
  • 42
  • 532
  • 579
iPC
  • 5,916
  • 3
  • 26
  • 38

3 Answers3

7

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
Aky
  • 1,777
  • 1
  • 14
  • 19
  • octant always return 0 and 1 only – iPC May 07 '14 at 07:27
  • @Mannnu I corrected a stupid mistake in the `atan2` expression in my pseudocode, make sure your code is correct. – Aky May 07 '14 at 07:35
  • 1
    All variables (except `octant`) ought to be `double` here. – Aky May 07 '14 at 07:36
  • @Aky.i corrected your code but still i have the same value – iPC May 07 '14 at 07:42
  • Please post your code in pastebin.com and post the link here in the comments – Aky May 07 '14 at 07:44
  • No offense, but you've kind of made a hodge podge of the code by mixing in stuff from the different answers you got. Can you just post the pie-chart image on an image hosting website (like picpaste) and post the link here. – Aky May 07 '14 at 08:57
  • No i have posted my own code.I have tried found out in between points to the two arc bounadaries but tat code is not working in ipad and mine iphone have works fine – iPC May 07 '14 at 09:02
  • Aky if u givr ur mail i will send ma project to you and tat can easily found out which one i have done in wrong – iPC May 07 '14 at 09:04
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/52178/discussion-between-aky-and-mannnu) – Aky May 07 '14 at 09:25
  • @Mannnu Good to know. I ended up adding complete working code to my answer anyway. – Aky May 08 '14 at 11:47
2

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.

Kaan Dedeoglu
  • 14,765
  • 5
  • 40
  • 41
  • 2
    +1 from me.. but in your `atan2` calculation you need to take into account that (0, 0) for the view is actually the upper-left corner. – Aky May 07 '14 at 07:40
  • @Kaan Dedeoglu. Actualy what you mean by this line of code self.bounds.size.width i got error while typeing the above line . – iPC May 07 '14 at 10:25
  • @Aky you are completely right, thanks for the correction, I added the point transformation and appropriate comments, take a look if you want to. – Kaan Dedeoglu May 07 '14 at 15:12
1

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.

kchromik
  • 1,348
  • 2
  • 13
  • 42
  • Kevin Chromik ,i want to detect touch inside the circle area. – iPC May 07 '14 at 06:26
  • @Mannnu, do you mean the inner cycle? So you could probably use a cycle `UIView` with another, smaller cycle `UIView` as a subview. – kchromik May 07 '14 at 06:29
  • 1
    My pie view will vary 4 to 8 portion how can i do to detect each pieces – iPC May 07 '14 at 06:31
  • ,please check the new image – iPC May 07 '14 at 06:34
  • @Mannnu I think it is easier, if you are using seperate `UIView`s for each piece. You will be way more flexible to add more piece, or change the sizes of them. Then, to detect the touch on one of them, you just need to add the touch detection for the specific new piece. – kchromik May 07 '14 at 06:40
  • i use only one view to draw all these portions. – iPC May 07 '14 at 06:44
  • Kevin Chromik ,your code snippet working fine for only four portion – iPC May 07 '14 at 07:14
  • Can you provide for the same sloution while the circle have 5 or 6 area. – iPC May 07 '14 at 10:26