2016-11-15 2 views
5

Я работаю над круговой диаграммой, и мне нужно добавить небольшой вид в конец выделенного круга (не тень) для указания на целевое значение. Могу ли я найти круговой (выделение) супер-вид x, y позиций на основе конечной точки хода?Как получить позицию надзора CAShapeLayer?

UIView->Circle (с использованием CAShapeLayer и UIBezierPath). Мне нужно получить позицию UIView на основе окончательного значения Circle.

Пожалуйста, обратитесь по ссылке (например, 23% с пунктирной линией) http://support.softwarefx.com/media/74456678-5a6a-e211-84a5-0019b9e6b500/large

Заранее спасибо! Need to find green end circle position

Update: Я попытался alexburtnik код, На самом деле я работаю на часовой стрелке графика в Objective C, но это не проблема. Я старался, как сказал алексбуртник, я считаю, что он отлично работает для анти-часовой мудрый график. Нам нужно внести некоторые изменения в код для работы по часовой стрелке, пожалуйста, дайте решение, если вы знаете.

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];} 

Please check my orignal graph

И я пытался, как esthepiking упоминалось, здесь я добавил код и скриншот

-(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];} 

Please check this result

+0

convertPoint: toView: должен сделать трюк – Alex

ответ

5

1. Математика

Давайте забудем о прошивке на мгновение и решить математическую задачу.

Задача: найти (x; y) точку для заданного α.

Решение: x = cos (α); у = sin (α)

math

2. Замена

Ваша точка не на 0 в терминах тригонометрии, а на π/2. Кроме того, ваш угол дуги определяется параметром прогресса, поэтому он приносит вам это:

х = соз (прогресс * 2 * π + π/2) = -sin (прогресс * 2 * π)
у = sin (прогресс * 2 * π + π/2) = соз (прогресс * 2 * π)

3. Теперь давайте вернемся к прошивке:

Поскольку в прошивке происхождения находится в верхнем левый угол, ось y вернется, вы должны преобразовать предыдущее решение таким образом:

х '= 0,5 * (1 + х)
у' = 0,5 * (1 - у)

Зеленый для математических координат, синий для IOS-системы, которую мы трансформированного координировать :

iOS

Теперь все, что вам нужно сделать, это умножить результат на ширину и высоту:

х '= 0,5 * ширина * (1 - SIN (прогресс * 2 * π))
у' = 0,5 * высота * (1 - сов (прогресс * 2 * π))

4 . Код:

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) 
} 

Редактировать

Если вы хотите, чтобы переключить его в направлении по часовой стрелке, просто умножить на угол -1:

х = соз (-Прогресс * 2 * π + π/2) = -sin (-Прогресс * 2 * π) = sin (прогресс * 2 * π)
у = sin (-Прогресс * 2 * π + π/2) = cos (-progress * 2 * π) = cos (прогресс * 2 * π)

x '= 0.5 * ширина * (1 + sin (прогресс * 2 * π))
у»= 0,5 * высота * (1 - соз (прогресс * 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) 
} 

Надеется, что это помогает.

+0

Спасибо за ответ! Фактически я работаю на часовом графике в объективе c, но это не проблема. Я пробовал, как вы упомянули, я считаю, что он отлично работает для борьбы с часами. Нам нужно внести некоторые изменения в код для работы по часовой стрелке, пожалуйста, дайте решение, если вы знаете. Я добавил свой код и скриншот в свой первоначальный вопрос выше. – Gopik

+0

Тогда ваш вопрос вводит в заблуждение, потому что скриншот не выглядит как индикатор хода по часовой стрелке. – alexburtnik

+0

Да, это была моя ошибка. Простите за это! Могли бы вы также иметь часы решения? – Gopik

2

Чтобы получить точку, вам нужно сделать немного бит математики.

Когда вы определяете дугу в UIBezierPath, вы передаете startAngle и endAngle. В этом случае, я считаю, вы хотите выровнять другой вид с концом штриха.

UIBezierPath(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) 

Теперь вам нужно взять синус и косинус этого endAngle параметра. Синус конечного угла даст вам позицию y в единичных координатах, а также в диапазоне [-1, 1], а косинус конечного угла даст вам положение x также в координатах единицы. Затем эти координаты можно масштабировать по размеру дуги и отрегулировать так, чтобы он соответствовал виду, а затем сдвигался по позиции зрения, чтобы получить положение конца хода.

Вот пример площадки для этого эффекта. Я выровнял UILabel, чтобы текст был центрирован над конечной точкой. Вставьте это в игровую площадку iOS, чтобы попробовать сами.

Demonstrating the centered view

import UIKit 
import PlaygroundSupport 

PlaygroundPage.current.needsIndefiniteExecution = true 

// Frame to wrap all of this inside of 
let frame = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300)) 
frame.backgroundColor = .white 

let radius = 100 as CGFloat 

// Outermost gray circle 
let grayCircle = CAShapeLayer() 
grayCircle.frame = frame.frame 
grayCircle.path = UIBezierPath(arcCenter: frame.center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI * 2), clockwise: true).cgPath 
grayCircle.strokeColor = UIColor.lightGray.cgColor 
grayCircle.lineWidth = 10 
grayCircle.fillColor = UIColor.clear.cgColor 

frame.layer.addSublayer(grayCircle) 

// Ending angle for the arc 
let endAngle = CGFloat(M_PI/5) 

// Inner green arc 
let greenCircle = CAShapeLayer() 
greenCircle.frame = frame.frame 
greenCircle.path = UIBezierPath(arcCenter: frame.center, radius: radius, startAngle: CGFloat(-M_PI_2), endAngle: endAngle, clockwise: false).cgPath 
greenCircle.strokeColor = UIColor.green.cgColor 
greenCircle.lineWidth = 10 
greenCircle.fillColor = UIColor.clear.cgColor 
greenCircle.lineCap = kCALineCapRound 

frame.layer.addSublayer(greenCircle) 

// Size for the text 
let width = 75 as CGFloat 
let height = 30 as CGFloat 

// 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 
var endX = (cos(endAngle)/2 + 0.5) 
var endY = (sin(endAngle)/2 + 0.5) 

// Scale the coordinates to match the diameter of the circle 
endX *= radius * 2 
endY *= radius * 2 

// Translate the coordinates based on the location of the frame 
endX -= frame.frame.origin.x 
endY -= frame.frame.origin.y 

// Vertically align the label 
endY += height 

// Setup the label 
let text = UILabel(frame: CGRect(x: endX, y: endY, width: width, height: height)) 
text.text = "Stroke end!" 
text.font = UIFont.systemFont(ofSize: 14) 
frame.addSubview(text) 

PlaygroundPage.current.liveView = frame 
+0

Спасибо за вашу помощь. Я добавил свой результат выше. Не могли бы вы проверить его. – Gopik

+0

@ user1591698 похоже, что у вас есть работа или, по крайней мере, довольно близко. Возможно, вам нужно избавиться от 'endY + = height' для вашего варианта использования. – esthepiking

+0

Да! Еще раз спасибо :) – Gopik

Смежные вопросы