我试图旋转一个UIImageView 360度,并在网上看了几个教程。我不能让它们都工作,UIView要么停止,要么跳转到一个新位置。
我怎样才能做到这一点呢?
我最近尝试的是:
[UIView animateWithDuration:1.0
delay:0.0
options:0
animations:^{
imageToMove.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){
NSLog(@"Done!");
}];
但如果我使用2*pi,它根本不会移动(因为它是相同的位置)。如果我尝试只做(180度),它可以工作,但如果我再次调用这个方法,它会向后旋转。
编辑:
[UIView animateWithDuration:1.0
delay:0.0
options:0
animations:^{
[UIView setAnimationRepeatCount:HUGE_VALF];
[UIView setAnimationBeginsFromCurrentState:YES];
imageToMove.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){
NSLog(@"Done!");
}];
也不管用。它会转到180度,暂停一秒钟,然后在重新开始之前重置回0度。
Swift 5使用关键帧动画的UIView扩展
这个方法允许我们直接使用UIView.AnimationOptions.repeat
public extension UIView {
func animateRotation(duration: TimeInterval, repeat: Bool, completion: ((Bool) -> ())?) {
var options = UIView.KeyframeAnimationOptions(rawValue: UIView.AnimationOptions.curveLinear.rawValue)
if `repeat` {
options.insert(.repeat)
}
UIView.animateKeyframes(withDuration: duration, delay: 0, options: options, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.25, animations: {
self.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
})
UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25, animations: {
self.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
})
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25, animations: {
self.transform = CGAffineTransform(rotationAngle: 3*CGFloat.pi/2)
})
UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25, animations: {
self.transform = CGAffineTransform(rotationAngle: 2*CGFloat.pi)
})
}, completion: completion)
}
}
@ram的回答很有帮助。以下是Swift版本的答案。
斯威夫特2
private func rotateImageView() {
UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, CGFloat(M_PI_2))
}) { (finished) -> Void in
if finished {
self.rotateImageView()
}
}
}
斯威夫特3、4、5所示
private func rotateImageView() {
UIView.animate(withDuration: 1, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: { () -> Void in
self.imageView.transform = self.imageView.transform.rotated(by: .pi / 2)
}) { (finished) -> Void in
if finished {
self.rotateImageView()
}
}
}
如果你想要做的只是无休止地旋转图像,这很好,而且非常简单:
NSTimeInterval duration = 10.0f;
CGFloat angle = M_PI / 2.0f;
CGAffineTransform rotateTransform = CGAffineTransformRotate(imageView.transform, angle);
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
imageView.transform = rotateTransform;
} completion:nil];
根据我的经验,这是完美的,但要确保你的图像能够在没有任何偏移的情况下围绕其中心旋转,否则图像动画将“跳跃”一旦它到达PI。
要改变旋转的方向,改变角度的符号(angle *= -1)。
@AlexPretzlav的更新评论让我重新审视了这个问题,我意识到当我写这篇文章时,我旋转的图像是沿着垂直和水平轴镜像的,这意味着图像确实只旋转了90度,然后重置,尽管它看起来像在继续旋转。
所以,如果你的图像像我的一样,这将工作得很好,然而,如果图像不是对称的,你会注意到90度后“snap”回到原来的方向。
要旋转非对称图像,最好使用公认的答案。
这些不太优雅的解决方案之一,如下所示,将真正旋转图像,但当动画重新启动时可能会有明显的口吃:
- (void)spin
{
NSTimeInterval duration = 0.5f;
CGFloat angle = M_PI_2;
CGAffineTransform rotateTransform = CGAffineTransformRotate(self.imageView.transform, angle);
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.imageView.transform = rotateTransform;
} completion:^(BOOL finished) {
[self spin];
}];
}
你也可以像@richard-j-ross-iii建议的那样只用块来做,但你会得到一个保留循环警告,因为块正在捕获自己:
__block void(^spin)() = ^{
NSTimeInterval duration = 0.5f;
CGFloat angle = M_PI_2;
CGAffineTransform rotateTransform = CGAffineTransformRotate(self.imageView.transform, angle);
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
self.imageView.transform = rotateTransform;
} completion:^(BOOL finished) {
spin();
}];
};
spin();
Kudos to Richard J. Ross III for the idea, but I found that his code wasn't quite what I needed. The default for options, I believe, is to give you UIViewAnimationOptionCurveEaseInOut, which doesn't look right in a continuous animation. Also, I added a check so that I could stop my animation at an even quarter turn if I needed (not infinite, but of indefinite duration), and made the acceleration ramp up during the first 90 degrees, and decelerate during the last 90 degrees (after a stop has been requested):
// an ivar for your class:
BOOL animating;
- (void)spinWithOptions:(UIViewAnimationOptions)options {
// this spin completes 360 degrees every 2 seconds
[UIView animateWithDuration:0.5
delay:0
options:options
animations:^{
self.imageToMove.transform = CGAffineTransformRotate(imageToMove.transform, M_PI / 2);
}
completion:^(BOOL finished) {
if (finished) {
if (animating) {
// if flag still set, keep spinning with constant speed
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
} else if (options != UIViewAnimationOptionCurveEaseOut) {
// one last spin, with deceleration
[self spinWithOptions: UIViewAnimationOptionCurveEaseOut];
}
}
}];
}
- (void)startSpin {
if (!animating) {
animating = YES;
[self spinWithOptions: UIViewAnimationOptionCurveEaseIn];
}
}
- (void)stopSpin {
// set the flag to stop spinning after one last 90 degree increment
animating = NO;
}
更新
我添加了处理请求重新开始旋转(startSpin)的能力,而之前的旋转正在结束(完成)。Github上的示例项目。
我已经开发了一个闪亮的动画框架,可以节省你的时间!
使用它可以很容易地创建这个动画:
private var endlessRotater: EndlessAnimator!
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
let rotationAnimation = AdditiveRotateAnimator(M_PI).to(targetView).duration(2.0).baseAnimation(.CurveLinear)
endlessRotater = EndlessAnimator(rotationAnimation)
endlessRotater.animate()
}
要停止这个动画,只需将nil设置为endlessRotater。
如果你感兴趣,请看看:https://github.com/hip4yes/Animatics
Nate上面的答案是理想的停止和开始动画,并提供了一个更好的控制。我很好奇为什么你的没用,而他的有用。我想在这里分享我的发现和一个更简单的代码版本,它可以使UIView连续动画而不停顿。
这是我用的代码,
- (void)rotateImageView
{
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[self.imageView setTransform:CGAffineTransformRotate(self.imageView.transform, M_PI_2)];
}completion:^(BOOL finished){
if (finished) {
[self rotateImageView];
}
}];
}
我使用“CGAffineTransformRotate”而不是“CGAffineTransformMakeRotation”,因为前者返回的结果是随着动画的进行而保存的。这将防止在动画期间视图的跳跃或重置。
另一件事是不要使用'UIViewAnimationOptionRepeat'因为在动画的结尾,在它开始重复之前,它会重置变换,使视图跳回它的原始位置。不是重复,而是递归,这样转换就永远不会重置为原始值,因为动画块实际上永远不会结束。
最后一件事是,你必须以90度(M_PI / 2)的步伐转换视图,而不是360度或180度(2*M_PI或M_PI)。因为变换是正弦和余弦值的矩阵乘法。
t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t
所以,假设你使用180度变换,cos(180)产生-1,使得视图每次都在相反的方向变换(如果你把变换的弧度值改为M_PI, Note-Nate的答案也会有这个问题)。360度转换只是要求视图保持在原来的位置,因此你根本看不到任何旋转。