我正在制作圆形图表,我需要在突出显示的圆圈(不是阴影)的末尾添加小视图以指向目标值。我可以根据笔画终点找到一个圆(突出显示)超视图x,y位置吗?
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
更新 我已经尝试过alexburtnik代码,实际上我正在研究目标c中的时钟图,但这不是问题。我试着像alexburtnik提到的那样,我相信它完全适用于反时钟图。我们需要对代码进行一些更改以便为顺时针方向工作,如果您知道,请提供解决方案。
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];}
我试着提到了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];}
答案 0 :(得分:5)
<强> 1。数学强>
让我们暂时忘掉iOS并解决数学问题。
问题:找到给定α的点(x; y)。
解决方案: x = cos(α); y = sin(α)
<强> 2。取代强>
你的起点在三角学方面不是0,而是在π/ 2 。您的弧角也是由进度参数定义的,因此它会引导您:
x = cos(进度* 2 *π+π/ 2)= -sin(进度* 2 *π)
y = sin(进度* 2 *π+π/ 2)= cos(进度* 2 *π)
第3。现在让我们回到iOS:
由于在iOS原点位于左上角,y轴被还原,您应该以这种方式转换上一个解决方案:
x'= 0.5 *(1 + x)
y'= 0.5 *(1 - y)
绿色代表数学坐标,蓝色代表iOS坐标系,我们将其转换为:
现在你需要做的就是将结果乘以宽度和高度:
x'= 0.5 *宽*(1-sin(进展* 2 *π))
y'= 0.5 *高度*(1 - cos(进度* 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
:
x = cos(-progress * 2 *π+π/ 2)= -sin(-progress * 2 *π)= sin(进度* 2 *π)
y = sin(-progress * 2 *π+π/ 2)= cos(-progress * 2 *π)= cos(进度* 2 *π)x'= 0.5 *宽*(1 + sin(进展* 2 *π))
y'= 0.5 *高度*(1 - cos(进度* 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)
}
希望这有帮助。
答案 1 :(得分:2)
为了明白这一点,你需要做一些数学运算。
在UIBezierPath中定义弧时,可以传入startAngle
和endAngle
。在这种情况下,我相信你想在中风结束时排列另一个视图。
UIBezierPath(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
现在您需要获取endAngle
参数的正弦和余弦。结束角度的正弦将给出单位坐标中的y位置,也就是在[-1,1]范围内,并且结束角度的余弦将在单位坐标中给出x位置。然后可以通过弧的大小来缩放这些坐标,并进行调整以使其与视图匹配,然后移动到视图的位置以获得笔划结束的位置。
以下是此效果的示例操场。我对齐了UILabel,因此文本在终点上居中。将其粘贴到iOS游乐场,自行试用。
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