我想在我的应用程序中来回摆动图像,类似于当你按下它时iPhone图标的摆动。最好的方法是什么?
这是我第一次尝试不使用动画GIF的动画。我认为这个想法是来回轻微旋转图像以产生摆动效果。我已经看过使用CABasicAnimation和CAKeyframeAnimation。 CABasicAnimation每次重复时都会创建一个抖动,因为它会跳转到from位置并且不会向内插回。 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];
或者可能有一个我只是缺少的完全简单的解决方案。感谢任何指针。谢谢!
答案 0 :(得分:92)
简单的方法:
#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;
}
}
可能需要玩时间和角度,但这应该让你开始。
编辑:我编辑了响应以添加代码,以便在完成后将项目恢复到原始状态。另请注意,您可以使用beginAnimations
上下文值将任何内容传递给start / stop方法。在这种情况下,它是摆动对象本身,因此您不必依赖特定的ivars,并且该方法可用于任何基于UIView的通用对象(即文本标签,图像等)
答案 1 :(得分:92)
Ramin的答案非常好,但是从OS4开始,在一个简单的函数中使用animateWithDuration也可以达到同样的效果。
(改编他的未来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
];
}
答案 2 :(得分:14)
您应该使用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];
}
答案 3 :(得分:9)
我编写了一个示例应用,尝试复制主屏幕抖动和图标移动:iPhone Sample Code: Tiles
答案 4 :(得分:3)
对于最近遇到过此帖子的人,并希望在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)
}
答案 5 :(得分:2)
我知道最简单的方法是使用Core Animation。基本上,您创建一个核心动画块,然后进行旋转变换和设置并重复计数。核心动画然后负责处理这种摆动效果所需的一切。
要启动Core Animation块,只需执行以下操作:
[UIView beginAnimations:@"any string as animationID" context:self];
[UIView setAnimationRepeatCount:10];
// rotate
[UIView commitAnimations];
未经测试。但也可能是你必须这样做:
[UIView setAnimationBeginsFromCurrentState:YES];
A.F.A.I.K。 setAnimationRepeatCount将具有动画完成,撤消,完成,撤消,完成,撤消,完成...的效果,与您指定的次数相同。所以你可能想先旋转到左边没有重复计数,然后从这一点开始摇晃重复计数。完成后,您可能希望旋转回身份变换(=不应用旋转和缩放)。
您可以通过使用
设置动画委托来链接动画[UIView setAnimationDelegate:self]
然后
[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
一旦动画停止,将调用该方法。有关如何实现动画停止时将调用的方法的信息,请参阅UIView类文档。基本上,在该方法中,您将使用新动画块但相同的上下文和动画ID执行下一步(即向后旋转或其他任何操作),然后(如果需要)指定另一个didStopSelector。
<强>更新强>
您可以查看:
[UIView setAnimationRepeatAutoreverses:YES];
这将自动来回摆动。
答案 6 :(得分:2)
您可以使用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"];
答案 7 :(得分:1)
晚会。我通常使用带阻尼的弹簧(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
}
您可以通过调整initialSpringVelocity
和SpringWithDamping
答案 8 :(得分:1)
根据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)
答案 9 :(得分:0)
Ramin给出的代码运行良好。但是如果您使用tabbar应用程序并移动到下一个标签项然后再次返回上一个标签项,您将看到您的视图已移至左侧,每个所以最好的做法是使用ViewWillAppear方法作为。
- (void)viewWillAppear:(BOOL)animated
{
UIView* item = self.view;
item.transform = CGAffineTransformIdentity;
}
因此,每次加载视图时,您都会在正确的位置找到动画。也可以使用此方法。
[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
答案 10 :(得分:0)
Swift 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()
}
答案 11 :(得分:0)
使用Swift 4+的最简单方法:
static func wobbleAnimation(button: UIButton) {
CATransaction.begin()
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.autoreverses = true
rotateAnimation.repeatCount = Float.greatestFiniteMagnitude
rotateAnimation.fromValue = CGFloat(-0.2)
rotateAnimation.toValue = CGFloat(0.2)
rotateAnimation.duration = 0.20
button.layer.add(rotateAnimation, forKey: nil)
CATransaction.commit()
}
static func stopWobbleAnimation(button: UIButton) {
button.layer.removeAllAnimations()
}
答案 12 :(得分:0)
从以上答案中,我得到了swift5版本:
func startWobble() {
let angle = 5.0 * Double.pi / 180.0;
self.transform = CGAffineTransform.identity.rotated(by: CGFloat(-angle));
UIView.animate(withDuration: 0.25, delay: 0, options: [.allowUserInteraction,.repeat,.autoreverse], animations: {
self.transform = CGAffineTransform.identity.rotated(by: CGFloat(angle));
}, completion: nil)
}
func stopWobble() {
UIView.animate(withDuration: 0.25, delay: 0, options: [.allowUserInteraction,.beginFromCurrentState,.curveLinear], animations: {
self.transform = CGAffineTransform.identity;
}, completion: nil)
}