Как повернуть объект вокруг произвольной точки? - PullRequest
9 голосов
/ 28 мая 2011

Я хочу вращать 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];

}

Ответы [ 4 ]

10 голосов
/ 18 мая 2012

Вы должны установить свои собственные anchorPoint

Сильно излишне использовать анимацию ключевого кадра для того, что действительно является изменением точки привязки .

Точка привязки - это точка, к которой применяются все преобразования, точка привязки по умолчанию - центр. Переместив опорную точку в (0,0), вы можете вместо этого повернуть слой из самого нижнего угла. Установив точку привязки на что-то, где x или y находится вне диапазона 0,0 - 1,0, вы можете сделать так, чтобы слой вращался вокруг точки, которая находится за пределами его границ.

Пожалуйста, прочитайте раздел о Геометрия слоя и преобразования в Руководстве по программированию базовой анимации для получения дополнительной информации. Это подробно описано с помощью изображений, которые помогут вам понять.


РЕДАКТИРОВАТЬ: Одна вещь, чтобы помнить

кадр вашего слоя (который также является кадр вашего зрения) вычисляется с использованием позиции, оценки и точки привязки. Изменение anchorPoint изменит место, где ваш вид появится на экране. Вы можете противостоять этому, повторно установив рамку после изменения точки привязки (это будет установить позицию для вас). В противном случае вы можете установить положение в точку, которую вы вращаете для себя. В документации (ссылка выше) также упоминается об этом.


Приложенный к вам код

Точка, которую вы назвали «транспортной точкой», должна быть обновлена, чтобы вычислить разницу между точкой поворота и нижним левым углом метки, деленную на ширину и высоту.

// 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 голосов
/ 29 мая 2011

Я решил опубликовать свое решение в качестве ответа.Он работает хорошо, примите, что у него нет кривой анимации старых решений (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"]; 
}
2 голосов
/ 28 мая 2011

CAKeyframeAnimation - правильный инструмент для этой работы.Большинство анимаций UIKit находятся между начальной и конечной точками.Средние точки не учитываются.CAKeyframeAnimation позволяет вам определять эти средние точки для обеспечения нелинейной анимации.Вам нужно будет указать соответствующий путь Безье для вашей анимации.Вы должны взглянуть на this example и тот, который указан в документации Apple, чтобы увидеть, как он работает.

0 голосов
/ 28 мая 2011

перевести, повернуть вокруг центра, перевести обратно.

...