I want to wobble an image back and forth in my application similar to how the iPhone icons wobble when you press down on it. What's the best way to do that?
我想在我的应用程序中来回晃动一个图像,类似于当你按下iPhone图标时的抖动。最好的方法是什么?
This is my first foray into animations that's not using an animated GIF. I think the idea is to slightly rotate the image back and forth to create the wobbling effect. I've looked at using CABasicAnimation and CAKeyframeAnimation. CABasicAnimation creates a jitter every time it repeats because it jumps to the from position and doesn't interpolate back. CAKeyframeAnimation seems like the solution except that I can't get it to work. I must be missing something. Here's my code using the CAKeyframeAnimation (which doesn't work):
这是我第一次尝试不使用动画GIF的动画。我认为这个想法是要稍微旋转一下图像来产生抖动的效果。我已经看过了使用CABasicAnimation和CAKeyframeAnimation。CABasicAnimation在每次重复时都会产生一种抖动,因为它会从位置跳到另一个位置,并且不会插值回来。CAKeyframeAnimation似乎是一个解决方案,但我不能让它工作。我一定是漏掉了什么。这是我使用CAKeyframeAnimation的代码(它不工作):
NSString *keypath = @"wobbleImage";
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
animation.duration = 1.0f;
animation.delegate = self;
animation.repeatCount = 5;
CGFloat wobbleAngle = 0.0872664626f;
NSValue *initial = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0f, 0.0f, 0.0f, 1.0f)];
NSValue *middle = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue *final = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:initial, middle, final, nil];
[imageView.layer addAnimation:animation forKey:keypath];
Or there could be a totally simpler solution that I'm just missing. Appreciate any pointers. Thanks!
或者有一种完全更简单的解决方案我只是没有。欣赏任何指针。谢谢!
91
Simple way to do it:
简单的方法:
#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)
CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5.0));
CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5.0));
itemView.transform = leftWobble; // starting point
[UIView beginAnimations:@"wobble" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:10];
[UIView setAnimationDuration:0.25];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(wobbleEnded:finished:context:)];
itemView.transform = rightWobble; // end here & auto-reverse
[UIView commitAnimations];
...
- (void) wobbleEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
if ([finished boolValue]) {
UIView* item = (UIView *)context;
item.transform = CGAffineTransformIdentity;
}
}
Probably have to play with timing and angles but this should get you started.
也许你得考虑时间和角度,但这应该能让你开始。
EDIT: I edited the response to add code to put the item back in its original state when done. Also, note that you can use the beginAnimations
context value to pass along anything to the start/stop methods. In this case it's the wobbling object itself so you don't have to rely on specific ivars and the method can be used for any generic UIView-based object (i.e. text labels, images, etc.)
编辑:我编辑响应以添加代码,以便在完成后将项目恢复到原始状态。另外,请注意,您可以使用beginanimation上下文值将任何东西传递给start/stop方法。在本例中,它是抖动对象本身,所以您不必依赖特定的ivars,该方法可以用于任何基于uiview的通用对象(例如文本标签、图像等)。
89
Ramin's answer was very good, but since OS4 the same effect can be achieved using animateWithDuration in one simple function too.
Ramin的回答非常好,但是由于OS4也可以在一个简单的函数中使用animateWithDuration来实现同样的效果。
(adapted his example for future googlers)
(为未来的谷歌员工树立了榜样)
#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)
- (void)startWobble {
itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5));
[UIView animateWithDuration:0.25
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
animations:^ {
itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5));
}
completion:NULL
];
}
- (void)stopWobble {
[UIView animateWithDuration:0.25
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear)
animations:^ {
itemView.transform = CGAffineTransformIdentity;
}
completion:NULL
];
}
13
You should use CAKeyframeAnimation
to make a smoother animation.
您应该使用CAKeyframeAnimation来制作更流畅的动画。
+ (void) animationKeyFramed: (CALayer *) layer
delegate: (id) object
forKey: (NSString *) key {
CAKeyframeAnimation *animation;
animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 0.4;
animation.cumulative = YES;
animation.repeatCount = 2;
animation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat: 0.0],
[NSNumber numberWithFloat: RADIANS(-9.0)],
[NSNumber numberWithFloat: 0.0],
[NSNumber numberWithFloat: RADIANS(9.0)],
[NSNumber numberWithFloat: 0.0], nil];
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion= NO;
animation.delegate = object;
[layer addAnimation:animation forKey:key];
}
9
I have written a sample app that attempts to replicate the home screen wobble and icon movement: iPhone Sample Code: Tiles
我编写了一个示例应用程序,试图复制主屏幕抖动和图标移动:iPhone样本代码:Tiles。
2
The easiest way I know is to use Core Animation. Basically, you create an Core Animation Block, then do an rotation transform and setup and repeat count. Core Animation then takes care of everything that's needed to do this wobbling effect.
我知道的最简单的方法是使用Core Animation。基本上,你创建一个核心动画块,然后做一个旋转变换和设置和重复计数。然后Core Animation就会处理所有需要的东西,以实现这个抖动效果。
To start an Core Animation block, just do:
要启动核心动画块,只需:
[UIView beginAnimations:@"any string as animationID" context:self];
[UIView setAnimationRepeatCount:10];
// rotate
[UIView commitAnimations];
not tested. But it can be that you will also have to do:
不测试。但是你也可以这样做:
[UIView setAnimationBeginsFromCurrentState:YES];
A.F.A.I.K. setAnimationRepeatCount will have the effect that the animation gets done, undone, done, undone, done, undone, done... as many times as you specify. So you may want to first rotate to left with no repeat count, and then from this point start wobbling with repeat count. When done, you may want to rotate back to the identity transform (= no rotation and scaling applied).
a.f.a.i.k。setAnimationRepeatCount会对动画效果产生影响,动画完成了,未完成,完成了,完成了,完成了,完成了……你指定的次数。所以你可能想先向左旋转没有重复计数,然后从这一点开始重复计数抖动。完成后,您可能想要返回到标识转换(=不使用旋转和缩放)。
You can chain animations by setting the animation delegate with
可以通过设置动画委托来链接动画
[UIView setAnimationDelegate:self]
and then
然后
[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
and as soon as the animation stops, that method will be called. See the UIView class documentation for how to implement that method that will be called when the animation stops. Basically, inside that method you would perform the next step (i.e. rotating back, or anything else), with an new animation block but same context and animation ID, and then (if needed) specify another didStopSelector.
一旦动画停止,这个方法就会被调用。请参阅UIView类文档,了解如何实现动画停止时将调用的方法。基本上,在该方法中,您将执行下一个步骤(例如,回滚或其他操作),使用一个新的动画块,但是使用相同的上下文和动画ID,然后(如果需要)指定另一个didStopSelector。
UPDATE:
更新:
You may want to check out:
你可能想要退房:
[UIView setAnimationRepeatAutoreverses:YES];
this will wobble back and forth automatically.
这将自动来回摆动。
2
You can create a not-endless wobble effect using the CAKeyframeAnimation
, like so:
您可以使用CAKeyframeAnimation创建一个不无休止的抖动效果,比如:
CGFloat degrees = 8.0;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 0.6;
animation.cumulative = YES;
animation.repeatCount = 1;
animation.values = @[@0.0,
@RADIANS(-degrees) * 0.25,
@0.0,
@RADIANS(degrees) * 0.5,
@0.0,
@RADIANS(-degrees),
@0.0,
@RADIANS(degrees),
@0.0,
@RADIANS(-degrees) * 0.5,
@0.0,
@RADIANS(degrees) * 0.25,
@0.0];
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion= YES;
[self.layer addAnimation:animation forKey:@"wobble"];
2
For anyone who has come across this posting more recently and would like to do the same in Swift, here is my translation:
对于最近看到这篇文章并想在Swift上做同样事情的人,以下是我的翻译:
func smoothJiggle() {
let degrees: CGFloat = 5.0
let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
animation.duration = 0.6
animation.cumulative = true
animation.repeatCount = Float.infinity
animation.values = [0.0,
degreesToRadians(-degrees) * 0.25,
0.0,
degreesToRadians(degrees) * 0.5,
0.0,
degreesToRadians(-degrees),
0.0,
degreesToRadians(degrees),
0.0,
degreesToRadians(-degrees) * 0.5,
0.0,
degreesToRadians(degrees) * 0.25,
0.0]
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.removedOnCompletion= true
layer.addAnimation(animation, forKey: "wobble")
}
func stopJiggling() {
jiggling = false
self.layer.removeAllAnimations()
self.transform = CGAffineTransformIdentity
self.layer.anchorPoint = CGPointMake(0.5, 0.5)
}
1
Late to the party. I'm typically using spring with damping (iOS7 and newer). In swift it looks like:
晚了些。我通常使用带阻尼的弹簧(iOS7和更新的)。在swift代码中,它看起来是:
sender.transform = CGAffineTransformMakeScale(1.2, 1.2)
UIView.animateWithDuration(0.30, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
sender.transform = CGAffineTransformMakeScale(1, 1)
}) { (Bool) -> Void in
//Do stuff when animation is finished
}
You can tweak the effect by adjusting the initialSpringVelocity
and SpringWithDamping
你可以通过调整初始速度和具有阻尼的弹簧来调整效果
1
Based on EsbenB's answer, but updated for Swift 3 and for rotation:
基于EsbenB的回答,但更新为Swift 3和旋转:
sender.transform = CGAffineTransform(rotationAngle: 12.0 * .pi / 180.0)
UIView.animate(withDuration: 0.60, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: .curveEaseInOut, animations: { () -> Void in
sender.transform = CGAffineTransform(rotationAngle: 0.0)
}, completion: nil)
0
Well the code given by Ramin works well.But if you use tabbar application and move to next tab item then again come back to previous tab item,you will see that your view has been moved to left,every time.So the best practice is that you use ViewWillAppear method as.
Ramin给出的代码运行得很好。但是如果您使用tabbar应用程序并移动到下一个选项卡项,然后再次返回到上一个选项卡项,您将看到每次视图都被移动到左边。所以最佳实践是使用ViewWillAppear方法as。
- (void)viewWillAppear:(BOOL)animated
{
UIView* item = self.view;
item.transform = CGAffineTransformIdentity;
}
so the every time view is loaded you will find you animation at the right place.And also use this method as well.
因此,每次加载视图时,你都会在正确的地方找到动画。也可以用这种方法。
[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
0
Swift 3
斯威夫特3
func startWiggling() {
deleteButton.isHidden = false
guard contentView.layer.animation(forKey: "wiggle") == nil else { return }
guard contentView.layer.animation(forKey: "bounce") == nil else { return }
let angle = 0.04
let wiggle = CAKeyframeAnimation(keyPath: "transform.rotation.z")
wiggle.values = [-angle, angle]
wiggle.autoreverses = true
wiggle.duration = randomInterval(0.1, variance: 0.025)
wiggle.repeatCount = Float.infinity
contentView.layer.add(wiggle, forKey: "wiggle")
let bounce = CAKeyframeAnimation(keyPath: "transform.translation.y")
bounce.values = [4.0, 0.0]
bounce.autoreverses = true
bounce.duration = randomInterval(0.12, variance: 0.025)
bounce.repeatCount = Float.infinity
contentView.layer.add(bounce, forKey: "bounce")
}
func stopWiggling() {
deleteButton.isHidden = true
contentView.layer.removeAllAnimations()
}