user1591698 - 9 months ago 49

iOS Question

I'm working on a circular chart, and I need to add small view at end of the highlighted circle (not shadow) for pointing at the target value. Can I find a circle (highlight) super view x,y positions based on the stroke end point?

`UIView->Circle`

`CAShapeLayer`

`UIBezierPath`

`UIView`

`Circle`

Please refer this link(like 23% with dotted line) http://support.softwarefx.com/media/74456678-5a6a-e211-84a5-0019b9e6b500/large

Thanks in advance!

Need to find green end circle position

Update:

I have tried alexburtnik code, Actually am working on clock-wise graph in objective c but that's not a problem here. I tried as alexburtnik mentioned, I believe it perfectly works for anti-clock wise graph. We need to make some change in code to work for Clockwise too, Please give solution if you know.

`CGFloat radiusCircle = (self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f);`

-(void)addTargetViewWithOptions:(CGFloat)progress andRadius:(CGFloat)radius{

CGFloat x = radius * (1 + (cos(M_PI * (2 * progress + 0.5))));

CGFloat y = radius * (1 - (sin(M_PI * (2 * progress + 0.5))));

UIView *targetView = [[UIView alloc]initWithFrame:CGRectMake(x, y, 40, 30)];

targetView.backgroundColor = [UIColor greenColor];

[self addSubview:targetView];}

And i tried as esthepiking mentioned, here i added code and screenshot

`-(void)addTargetView{`

CGFloat endAngle = -90.01f;

radiusCircle = (self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f);

endAngleCircle = DEGREES_TO_RADIANS(endAngle);//-1.570971

// Size for the text

CGFloat width = 75;

CGFloat height = 30;

// Calculate the location of the end of the stroke

// Cos calculates the x position of the point (in unit coordinates)

// Sin calculates the y position of the point (in unit coordinates)

// Then scale this to be on the range [0, 1] to match the view

CGFloat endX = (cos(endAngleCircle) / 2 + 0.5);

CGFloat endY = (sin(endAngleCircle) / 2 + 0.5);

// Scale the coordinates to match the diameter of the circle

endX *= radiusCircle * 2;

endY *= radiusCircle * 2;

// Translate the coordinates based on the location of the frame

endX -= self.frame.origin.x;

endY -= self.frame.origin.y;

// Vertically align the label

endY += height;

// Setup the label

UIView *targetView = [[UIView alloc]initWithFrame:CGRectMake(endX, endY, width, height)];

targetView.backgroundColor = [UIColor redColor];

[self addSubview:targetView];}

Answer Source

**1. Math**

Let's forget about iOS for a moment and solve a math problem.

**Problem:** find (x;y) point for a given α.

**Solution:** x = cos(α); y = sin(α)

**2. Substitution**
Your starting point is not at 0 in terms of trigonometry, but rather at **π/2**. Also your arc angle is defined by progress parameter, so it brings you to this:

x = cos(progress * 2 * π + π/2) = -sin(progress * 2 * π)

y = sin(progress * 2 * π + π/2) = cos(progress * 2 * π)

**3. Now let's return to iOS:**

Since in iOS origin is in the top left corner an y axis is reverted you should convert the previous solution this way:

x' = 0.5 * (1 + x)

y' = 0.5 * (1 - y)

Green is for mathematical coordinates, blue is for iOS coordinate system, which we transformed to:

Now everything you need to do is to multiply the result by width and height:

x' = 0.5 * width * (1 - sin(progress * 2 * π))

y' = 0.5 * height * (1 - cos(progress * 2 * π))

**4. Code:**

```
func point(progress: Double, radius: CGFloat) -> CGPoint {
let x = radius * (1 - CGFloat(sin(progress * 2 * Double.pi))))
let y = radius * (1 - CGFloat(cos(progress * 2 * Double.pi))))
return CGPoint(x: x, y: y)
}
```

**Edit**

If you want to switch it to clockwise direction, just multiply angle by `-1`

:

x = cos(-progress * 2 * π + π/2) = -sin(-progress * 2 * π) = sin(progress * 2 * π)

y = sin(-progress * 2 * π + π/2) = cos(-progress * 2 * π) = cos(progress * 2 * π)x' = 0.5 * width * (1 + sin(progress * 2 * π))

y' = 0.5 * height * (1 - cos(progress * 2 * π))

```
func point(progress: Double, radius: CGFloat) -> CGPoint {
let x = radius * (1 + CGFloat(sin(progress * 2 * Double.pi))))
let y = radius * (1 - CGFloat(cos(progress * 2 * Double.pi))))
return CGPoint(x: x, y: y)
}
```

Hope this helps.