提问人:Victor Engel 提问时间:9/25/2023 更新时间:9/26/2023 访问量:66
以类似于 CGRect、CGPoint 等的方式转换 UIBezierPath
Convert UIBezierPath in similar way to CGRect, CGPoint, etc
问:
UIView
具有在坐标空间之间转换点、矩形等的功能。我需要为 .举例来说,考虑两个视图,以及 .我们在坐标空间中有 。我们可以计算:UIBezierPaths
viewA
viewB
rectA : CGRect
viewA
let rectB = viewB.convert(rectA, from:viewA)
现在假设我们从矩形创建一个路径:
let pathA = UIBezierPath(rect: rectA)
问题是,如何计算.我们可以简单地说:rectB
let pathB = UIBezierPath(rect: rectB)
但这只有在我们有 .如果我们只有“pathA”怎么办?rectB
我想要做到这一点,我们需要一个转换。然后我们可以做:aTob
var pathB = pathA
pathB.apply(aTob)
所以问题归结为如何计算.为简单起见,假设没有旋转。aTob
因此,绘图应在屏幕上以与绘图相同的形状和位置绘制路径。pathA
viewA
pathB
viewB
答:
1赞
HangarRash
9/25/2023
#1
一种解决方案是使用转换转换在贝塞尔路径上调用 apply()。
您可以通过将 的原点转换为 的坐标空间来计算变换所需的 x 和 y。viewB
viewA
以下是一些可以放入 Swift Playground 的示例代码:
// Some setup
let rectA = CGRect(x: 40, y: 60, width: 200, height: 100)
let rectB = CGRect(x: 10, y: 80, width: 200, height: 100)
let viewA = UIView(frame: rectA)
let viewB = UIView(frame: rectB)
// Create pathA
let pathA = UIBezierPath(rect: rectA)
// Calculate the offset needed to move from viewA to viewB
let offset = viewA.convert(CGPoint.zero, from: viewB)
let tranform = CGAffineTransform(translationX: offset.x, y: offset.y)
// Copy pathA
let pathB = UIBezierPath()
pathB.append(pathA)
// Shift pathB
pathB.apply(tranform)
// Check result
print(pathA)
print(pathB)
输出:
<UIBezierPath: 0x600002c31e80; <MoveTo {40, 60}>,
<LineTo {140, 60}>,
<LineTo {140, 160}>,
<LineTo {40, 160}>,
<Close>
<UIBezierPath: 0x600002c12280; <MoveTo {10, 80}>,
<LineTo {110, 80}>,
<LineTo {110, 180}>,
<LineTo {10, 180}>,
<Close>
我相信这就是你要找的。的计算可能是倒退的。如果结果方向错误,请将该行更改为:offset
pathB
offset
let offset = viewB.convert(CGPoint.zero, from: viewA)
下面是用于将视图从一个视图转换为另一个视图的扩展:UIView
UIBezierPath
extension UIView {
private func adjust(_ path: UIBezierPath, by offset: CGPoint) -> UIBezierPath {
let transform = CGAffineTransform(translationX: offset.x, y: offset.y)
let result = UIBezierPath()
result.append(path)
result.apply(tranform)
return result
}
func convert(_ path: UIBezierPath, from view: UIView) -> UIBezierPath {
let offset = convert(CGPoint.zero, from: view)
return adjust(path, by: offset)
}
func convert(_ path: UIBezierPath, to view: UIView) -> UIBezierPath {
let offset = convert(CGPoint.zero, to: view)
return adjust(path, by: offset)
}
}
此答案假定两个视图都没有应用非标识转换。根据 OP 自己的答案,显然其中一个或两个视图可能具有非身份转换。
0赞
Victor Engel
9/26/2023
#2
使用像 HangarRash 发布的扩展一样的扩展,这是一个在两个视图具有相同超视图时有效的解决方案。
func convert(_ path: UIBezierPath, from view: UIView) -> UIBezierPath {
path.apply(view.transform)
// we now have a path in view's superview's coordinates. Let's adjust by the origin.
let viewTransform = CGAffineTransform(translationX: view.frame.origin.x, y: view.frame.origin.y)
path.apply(viewTransform)
// now the path is adjusted by view's origin. Next we need to subtract self's origin
let selfTranslationTransform = CGAffineTransform(translationX: -self.frame.origin.x, y: -self.frame.origin.y)
path.apply(selfTranslationTransform)
// now adjust by self's transform
path.apply(self.transform.inverted())
return path
}
评论
0赞
HangarRash
9/26/2023
这如何扩展贝塞尔路径?你最终在这里做的就是翻译,你声称没有回答你的问题。我现在很困惑。
0赞
Victor Engel
9/26/2023
path.apply(view.transform) 在参数中的视图上应用任何比例。并且,path.apply(self.transform.inverted()) 应用了 self 的倒置变换,该变换也可能具有比例。这是使用随机缩放因子随机定位的视图进行测试的。如果有旋转,则失败。
0赞
HangarRash
9/26/2023
我现在意识到我误读了你的答案。第一个和最后一个(反转)转换来自两个不同的视图。我发表了我的第一条评论,认为他们来自同一个观点。因此,如果两个视图都没有非身份转换,我目前的答案是有效的。你的问题并不像它可能的那样清楚。您没有提到对视图应用了转换的视图。您只谈到了视图矩形和调整两个矩形之间的贝塞尔路径。
0赞
Victor Engel
9/26/2023
我提到了不同的坐标空间。我提到的两个矩形具有不同的坐标空间。我直截了当地说了。什么决定了坐标空间?原点和转换的位置。可以通过平移、缩放和旋转来修改或初始化转换。我说为了简单起见,假设没有旋转。剩下的就是另外两个了。再清楚不过了。
0赞
HangarRash
9/26/2023
是的,经过进一步的评论,这已经非常清楚了。作为作者,您确切地知道您的问题意味着什么。作为读者,并非所有这些细节都那么清楚。很高兴你解决了你的问题。
评论
UIBezierPath
有一个apply()
方法,它采用 ;创建缩放转换(从 size of 转换为 size of )是否能获得预期的结果?CGAffineTransform
viewA
viewB
viewB
viewA