繁体   English   中英

如何获得CAShapeLayer的超级视图位置?

[英]How to get CAShapeLayer superview position?

我正在制作圆形图表,我需要在突出显示的圆圈(不是阴影)的末尾添加小视图以指向目标值。 我可以根据笔画终点找到一个圆(高亮)超视图x,y位置吗?

UIView->Circle (使用CAShapeLayerUIBezierPath )。 我需要根据结束Circle笔划值获得UIView的位置。

请参考此链接(如带有虚线的23%) http://support.softwarefx.com/media/74456678-5a6a-e211-84a5-0019b9e6b500/large

提前致谢! 需要找到绿色圆圈位置

更新:我已经尝试了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];} 

请检查此结果

数学

让我们暂时忘掉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坐标系,我们将其转换为:

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

希望这可以帮助。

为了明白这一点,你需要做一些数学运算。

在UIBezierPath中定义弧时,传入startAngleendAngle 在这种情况下,我相信你想要在笔画结束时排列另一个视图。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM