如何围绕任意点旋转物体?

9

我想以圆形方式围绕任意点旋转UILabel,而不是直线。这是我的代码。最终点是完美的,但它通过起始点和结束点之间的直线。

- (void)rotateText:(UILabel *)label duration:(NSTimeInterval)duration degrees:(CGFloat)degrees {
    /* Setup the animation */
    [UILabel beginAnimations:nil context:NULL];
    [UILabel setAnimationDuration:duration];


    CGPoint rotationPoint = CGPointMake(160, 236);
    CGPoint transportPoint = CGPointMake(rotationPoint.x - label.center.x, rotationPoint.y - label.center.y);

    CGAffineTransform t1 = CGAffineTransformTranslate(label.transform, transportPoint.x, -transportPoint.y);
    CGAffineTransform t2 = CGAffineTransformRotate(label.transform,DEGREES_TO_RADIANS(degrees));
    CGAffineTransform t3 = CGAffineTransformTranslate(label.transform, -transportPoint.x, +transportPoint.y);

    CGAffineTransform t4 = CGAffineTransformConcat(CGAffineTransformConcat(t1, t2), t3);
    label.transform = t4;   

    /* Commit the changes */
    [UILabel commitAnimations];

}

1
请查看博客文章http://www.informit.com/blogs/blog.aspx?uk=Ask-Big-Nerd-Ranch-Rotating-an-iPhone-View-Around-a-Point - Jhaliya - Praveen Sharma
4个回答

9

你应该设置自己的anchorPoint

使用关键帧动画来实现一个锚点的变化是非常冗余的。

锚点是应用所有变换的点,默认的锚点是中心点。通过将锚点移动到(0,0),您可以使图层从最底部的角度旋转。通过将锚点设置为某个值,其中xy在0.0-1.0范围之外,您可以使图层围绕其边界之外的点旋转。

请阅读核心动画编程指南中有关图层几何和变换的部分,以获取更多信息。它详细介绍了这一点,并提供了图片以帮助您理解。


编辑:需要记住的一件事

您的图层框架(也是视图框架)是使用位置、边界和锚点计算的。更改锚点将更改视图在屏幕上显示的位置。您可以通过在更改锚点后重新设置框架(这将为您设置位置)来抵消此效果。否则,您可以自己将位置设置为旋转到的点。文档(链接如上)也提到了这一点。


应用于您的代码

您所称呼的“transportPoint”点应该被更新,以计算旋转点与标签左下角之间的差值除以宽度和高度。

// Pseudocode for the correct anchor point
transportPoint = ( (rotationX - labelMinX)/labelWidth,
                   (rotationX - labelMinY)/labelHeight )

我还将旋转点作为参数添加到了你的方法中。完整更新后的代码如下:

#define DEGREES_TO_RADIANS(angle) (angle/180.0*M_PI)

- (void)rotateText:(UILabel *)label 
       aroundPoint:(CGPoint)rotationPoint 
          duration:(NSTimeInterval)duration 
           degrees:(CGFloat)degrees {

    /* Setup the animation */
    [UILabel beginAnimations:nil context:NULL];
    [UILabel setAnimationDuration:duration];

    // The anchor point is expressed in the unit coordinate
    // system ((0,0) to (1,1)) of the label. Therefore the 
    // x and y difference must be divided by the width and 
    // height of the label (divide x difference by width and 
    // y difference by height).
    CGPoint transportPoint = CGPointMake((rotationPoint.x - CGRectGetMinX(label.frame))/CGRectGetWidth(label.bounds),
                                         (rotationPoint.y - CGRectGetMinY(label.frame))/CGRectGetHeight(label.bounds));

    [label.layer setAnchorPoint:transportPoint];
    [label.layer setPosition:rotationPoint]; // change the position here to keep the frame 
    [label.layer setTransform:CATransform3DMakeRotation(DEGREES_TO_RADIANS(degrees), 0, 0, 1)];   

    /* Commit the changes */
    [UILabel commitAnimations];
}

8

我决定将我的解决方案发布为答案。它可以正常工作,但它没有旧的解决方案的曲线动画(UIViewAnimationCurveEaseOut),但我可以解决这个问题。

#define DEGREES_TO_RADIANS(angle) (angle / 180.0 * M_PI)

- (void)rotateText:(UILabel *)label duration:(NSTimeInterval)duration degrees:(CGFloat)degrees {
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddArc(path,nil, 160, 236, 100, DEGREES_TO_RADIANS(0), DEGREES_TO_RADIANS(degrees), YES);

    CAKeyframeAnimation *theAnimation;

    // animation object for the key path
    theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    theAnimation.path=path;
    CGPathRelease(path);

    // set the animation properties
    theAnimation.duration=duration;
    theAnimation.removedOnCompletion = NO; 
    theAnimation.autoreverses = NO;
    theAnimation.rotationMode = kCAAnimationRotateAutoReverse;
    theAnimation.fillMode = kCAFillModeForwards;

    [label.layer addAnimation:theAnimation forKey:@"position"]; 
}

+1:感谢您发布解决方案!这将来对其他人很有帮助。 - jscs
同样的问题,谢谢你的解决方案。另外,你解决了缓动的问题吗?我正在尝试做一个类似于UIScrollView但是循环的东西,如果有进一步的帮助将会非常棒。 - ymutlu
最后我使用了其他方法,但是为了方便起见,您可以连续创建一个主旋转和一个缓入缓出旋转。 - Tibidabo
使用关键帧对于这种情况过于复杂。请看一下我的答案,我为旋转设置了一个自定义锚点。它的工作方式与您问题中的代码类似,但不需要来回平移和连接多个变换。 - David Rönnqvist

2

CAKeyframeAnimation 是处理这个任务的正确工具。大多数UIKit动画只涉及起始点和终止点,而中间点则不被考虑。CAKeyframeAnimation允许您定义这些中间点以提供非线性动画。您需要为动画提供适当的贝塞尔路径。您可以查看这个示例以及Apple文档中提供的其他示例来了解它的工作原理。


谢谢你的回答,我已经通过苹果示例中的CAKeyframeAnimation自己解决了问题。 - Tibidabo
我认为设置锚点是围绕任意点旋转的工作。这就是锚点的作用。使用关键帧动画过于复杂,不是“这项工作的正确工具”。 - David Rönnqvist

-1

平移,绕中心旋转,再平移回来。


我认为我正在做的就是这样,但它没有经过一个圆形路径,而是一条直线。 - Tibidabo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接