swift 以类似于CGRect、CGPoint等的方式转换UIBezierPath

klr1opcd  于 2023-09-30  发布在  Swift
关注(0)|答案(2)|浏览(105)

UIView具有转换点、矩形等的函数。在坐标空间之间。我需要对UIBezierPaths做同样的事情。作为说明,考虑两个视图viewAviewB。我们在viewA的坐标空间中有rectA : CGRect。我们可以计算:

let rectB = viewB.convert(rectA, from:viewA)

现在假设我们从rect创建一个路径:

let pathA = UIBezierPath(rect: rectA)

问题是,如何计算rectB。我们可以简单地:

let pathB = UIBezierPath(rect: rectB)

但只有当我们有rectB时才有效。如果我们只有“路径A”呢?
我想要做到这一点,我们需要一个转换aTob。然后我们可以做:

var pathB = pathA
pathB.apply(aTob)

所以问题归结为如何计算aTob。为简单起见,假设不存在旋转。
因此,在viewA中绘制pathA应该在屏幕上绘制与在viewB中绘制pathB相同形状和位置的路径。

nnsrf1az

nnsrf1az1#

一种解决方案是使用平移变换在贝塞尔曲线路径上调用apply()
您可以通过将viewB的原点转换为viewA的坐标空间来计算变换所需的x和y。
以下是一些可以放在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自己的回答,显然其中一个或两个视图可能具有非恒等变换。

ehxuflar

ehxuflar2#

使用像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
}

相关问题