3

可能重复:
旋转 CGPath 而不改变其位置

我搜索并测试了几个小时的各种代码,但我无法让它工作。

我正在将随机位置的任意 UIBezierPath 添加到 CAShapeLayer,该 CAShapeLayer 将添加到视图中。我需要旋转路径,以便我可以处理设备旋转。我可以旋转图层而不是路径。我只需要旋转结果。

我已经有了通过缩放和平移来处理转换贝塞尔路径的方法。它工作得很好,但现在我需要简单地向左或向右旋转 90 度。

关于如何做到这一点的任何建议?

基本代码:

UIBezierPath *path = <create arbitrary path>
CAShapeLayer *layer = [CAShapeLayer layer];
[self addPathToLayer:layer
             fromPath:path];

// I could get the center of the box but where is the box center for the view it is in?
// CGRect box = CGPathGetPathBoundingBox(path.CGPath);

// layer.anchorPoint = ? How to find the center of the box for the anchor point?

// Rotating here appears to rotate around 0,0 of the view
layer.transform = CATransform3DMakeRotation(DegreesToRadians(-90), 0.0, 0.0, 1.0);

我看到以下帖子: BezierPath Rotation in a UIView

我想我可以按原样旋转,然后将路径转换回原位。我只需要弄清楚翻译值是什么。

我还应该说明,我在尝试旋转后看到的是图像在某处移出屏幕。我尝试旋转 25 度来查看运动,它围绕视图的原点 0,0 旋转,这样如果我旋转 90 度,图像就会在屏幕外。我在不旋转设备的情况下运行这些测试 - 只是为了看看旋转是如何工作的。

更新#1 - 12/4/2012:出于某种奇怪的原因,如果我将位置设置为我凭经验发现的值,它将旋转后的贝塞尔路径移动到正确的位置:

layer.position = CGPointMake(280, 60);

该值是从启动/停止应用程序和进行调整的猜测。我不知道为什么我需要调整旋转位置。锚点应位于图层的中心。但是,我确实发现 CAShapeLayer 的框架和位置都是零,即使设置了路径,而且路径在视图中的正确位置也是如此。当旋转 +90 度时,280、60 位置将路径移动到路径边界框的中心。如果我改变旋转值,我需要调整位置。我不应该手动调整。

我认为最后的手段是以某种方式将贝塞尔路径转换为图像,然后添加它。我发现如果我将图层内容设置为图像,然后旋转,它会围绕其中心点旋转,而无需进行位置调整。设置路径并非如此。

更新 #2 12/4/2012 - 我尝试设置框架并通过摆弄我将其置于中心位置,如下所示:

CGRect box = CGPathGetPathBoundingBox(path.CGPath);
CGRect rect = CGRectMake(0, 0, box.origin.x + (3.5 * box.size.width), box.origin.y + (3.5 * box.size.height));
layer.frame = rect;
layer.transform = CATransform3DMakeRotation(DegreesToRadians(90), 0.0, 0.0, 1.0);

为什么要乘以 3.5?我没有任何线索。我发现添加大约 3.5 倍于盒子大小的盒子原点会将旋转的 CAShapeLayer 路径移动到它应该在的位置。

必须有更好的方法来做到这一点。这是一个比我之前的帖子更好的解决方案,因为帧大小不取决于旋转角度。我只是不知道为什么需要将框架设置为我设置的值。我认为它应该是 CGRectMake(0, 0, box.origin.x + (box.size.width / 2), box.origin.y + (box.size.height / 2));

但是,它将图像向左移动太多。

我发现的另一个线索是,如果我设置[self view].frame的frame(整个父视图的frame,也就是iPhone的屏幕),然后旋转,旋转点就是屏幕的中心,一个路径/图像围绕该中心点运行。这就是为什么我尝试将框架移动到路径中心的位置,以便它围绕盒子中心运行。

UPDATE #3 12/4/2012 - 我试图将图层渲染为图像。但是,似乎仅设置图层的路径并不能使其成为图层中的“图像”,因为它是空的

    CGRect box = CGPathGetPathBoundingBox(path.CGPath);
    layer.frame = box;

    UIImage *image = [ImageHelper imageFromLayer:layer]; // ImageHelper library I created  

    CAShapeLayer *newLayer = [CAShapeLayer layer];
    newLayer.frame = CGRectMake(box.origin.x, box.origin.y, image.size.width, image.size.height);
    newLayer.contents = (id) image.CGImage;

看起来用它的路径集旋转层与简单地旋转贝塞尔路径本身没有什么不同。我将回到旋转贝塞尔路径,看看我是否可以摆弄位置元素或其他东西。一定有办法解决这个问题。

目标:在最初创建它的视图中围绕其中心点旋转 UIBezierPath。

UPDATE #4 12/4/2012 - 我运行了一系列测试来测量翻译所需的值,以便将 UIBezierPath 放置在其先前的中心位置。

CGAffineTransform rotate = CGAffineTransformMakeRotation(DegreesToRadians(-15));
[path applyTransform:rotate];

// CGAffineTransform translate = CGAffineTransformMakeTranslation(-110, 70); // -45
CGAffineTransform translate = CGAffineTransformMakeTranslation(-52, -58); // -15
[path applyTransform:translate];

但是,x/y 平移的比率不对应,因此我无法根据角度推断需要什么平移。似乎“CGAffineTransformMakeRotation”使用了一些任意锚点来进行旋转,目前看来可能是(viewWidth / 2, 0)。我正在使这比需要的困难得多。我缺少一些东西来进行简单的旋转以保持中心点。我只需要将路径向左或向右“旋转”90 度。

UPDATE #5 12/4/2012 - 运行附加测试后,旋转 UIBezierPath 的锚点似乎是绘制所有点的原点。在这种情况下,原点是 0,0,所有点都相对于该点。因此,它应用了旋转,旋转围绕原点发生,这就是为什么路径在 -90 上向右上移动,在 90 上左上移动的原因。我需要以某种方式将旋转的锚点设置为中心所以它围绕中心“旋转”,而不是原始原点。在这一问题上花了 12 个小时。

4

1 回答 1

5

经过一些详细的分析并在纸上绘制边界框后,我发现我的断言 0,0 的原点是正确的。

解决这个问题的方法是将路径(底层矩阵)平移到原点,边界框的中心位于原点,旋转,然后将路径平移回其原始位置。

这是将 UIBezierPath 旋转 90 度的方法:

CGAffineTransform translate = CGAffineTransformMakeTranslation(-1 * (box.origin.x + (box.size.width / 2)), -1 * (box.origin.y + (box.size.height / 2)));
[path applyTransform:translate];

CGAffineTransform rotate = CGAffineTransformMakeRotation(DegreesToRadians(90));
[path applyTransform:rotate];

translate = CGAffineTransformMakeTranslation((box.origin.x + (box.size.width / 2)), (box.origin.y + (box.size.height / 2)));
[path applyTransform:translate];

插入-90度以向另一个方向旋转。

当将设备从纵向旋转到横向时,可以使用此公式,反之亦然。

我仍然认为这不是理想的解决方案,但结果是我现在需要的。

如果有人对此有更好的解决方案,请发布。

2012 年 12 月 7 日更新 - 我发现我认为是最好的解决方案,而且非常简单,尽管它会很简单。我没有在贝塞尔路径上使用旋转、平移和缩放方法,而是将点数组提取为 CGPoint 对象,并根据视图大小和方向按需缩放/平移它们。然后我创建一个新的贝塞尔路径并将图层设置为此路径。

结果是完美的缩放、平移、旋转。

于 2012-12-05T03:41:03.563 回答