UIView 摇动动画
Posted
技术标签:
【中文标题】UIView 摇动动画【英文标题】:UIView shake animation 【发布时间】:2010-10-02 05:52:55 【问题描述】:我正在尝试在按下按钮时使 UIView 抖动。
我正在修改我在http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/ 上找到的代码。
但是,通过尝试修改以下代码来摇动 UIView,它不起作用:
- (void)animate
const int numberOfShakes = 8;
const float durationOfShake = 0.5f;
const float vigourOfShake = 0.1f;
CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animation];
CGRect frame = lockView.frame;
CGMutablePathRef shakePath = CGPathCreateMutable();
CGPathMoveToPoint(shakePath, NULL, CGRectGetMinX(frame), CGRectGetMinY(frame));
for (int index = 0; index < numberOfShakes; ++index)
CGPathAddLineToPoint(shakePath, NULL, CGRectGetMinX(frame) - frame.size.width * vigourOfShake, CGRectGetMinY(frame));
CGPathAddLineToPoint(shakePath, NULL, CGRectGetMinX(frame) + frame.size.width * vigourOfShake, CGRectGetMinY(frame));
CGPathCloseSubpath(shakePath);
shakeAnimation.path = shakePath;
shakeAnimation.duration = durationOfShake;
[lockView.layer addAnimation:shakeAnimation forKey:@"frameOrigin"];
【问题讨论】:
【参考方案1】:我写了那篇文章。这对于 UIView 来说太过分了,而且参数是针对 OSX 应用程序的。改为这样做。
CABasicAnimation *animation =
[CABasicAnimation animationWithKeyPath:@"position"];
[animation setDuration:0.05];
[animation setRepeatCount:8];
[animation setAutoreverses:YES];
[animation setFromValue:[NSValue valueWithCGPoint:
CGPointMake([lockView center].x - 20.0f, [lockView center].y)]];
[animation setToValue:[NSValue valueWithCGPoint:
CGPointMake([lockView center].x + 20.0f, [lockView center].y)]];
[[lockView layer] addAnimation:animation forKey:@"position"];
您必须使用 duration 和 repeatCount 参数以及 from 和 to 值中到中心的 x 距离,但它应该可以满足您的需求。我希望这会有所帮助。如果您有任何问题,请告诉我。
---
Swift 3.0
let midX = lockView.center.x
let midY = lockView.center.y
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.06
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = CGPoint(x: midX - 10, y: midY)
animation.toValue = CGPoint(x: midX + 10, y: midY)
layer.add(animation, forKey: "position")
【讨论】:
谢谢,我使用你的答案在 Xamarin ios 中构建它如果有人想使用它,这里是:gist.github.com/jorwan/1ed57459c7b01b5a5b1135219e6219cf @Matt 如果我想随机摇动,UIView 会在每次摇动时以随机方向移动怎么办? @EhteshamHasan 它不可能是真正随机的。位置/点必须在一个范围内,您可能不希望它在该范围的极端(例如角落到角落)之间摇摆(尽管这取决于您)。简单的答案是将可能的位置(CGPoints)放在一个数组中,并为一个随机数生成器播种,该生成器提供的索引小于数组中的点数,然后使用我的回答中描述的相同技术将位置移动到该点。 【参考方案2】:我更喜欢这种具有良好弹性行为的解决方案,非常适合错误密码摇动动画。
view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^
view.transform = CGAffineTransformIdentity;
completion:nil];
斯威夫特 3
extension UIView
func shake()
self.transform = CGAffineTransform(translationX: 20, y: 0)
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 1, options: .curveEaseInOut, animations:
self.transform = CGAffineTransform.identity
, completion: nil)
【讨论】:
你怎么重复这个?它现在只发生一次。 如果我对 OP 的理解正确,他想要一个简短的摇动动画。由于在现实生活中摇晃运动会随着时间的推移产生摩擦并减慢速度,因此我发现我的解决方案最合适。如果您想让它摇得更久,请尝试使用damping 和initialVelocity 参数。如果您想无限期地重复它,请使用其他解决方案之一。 这个动画是最好的,同意。只是想重复几次,仅此而已。 如果你只是想扩展它,尝试使用 duration、damping 和 initialVelocity 值。 这个解决方案要好得多,因为可以使用完成处理程序。【参考方案3】:这是我的漂亮而简单的版本 这模拟了当您登录不正确时在 Mac OS X 上的震动。如果您愿意,可以将其添加为 UIView 上的类别。
@implementation UIView (DUExtensions)
- (void) shake
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.duration = 0.6;
animation.values = @[ @(-20), @(20), @(-20), @(20), @(-10), @(10), @(-5), @(5), @(0) ];
[self.layer addAnimation:animation forKey:@"shake"];
@end
动画值是视图当前位置的 x 偏移量。正值向右移动视图,负值向左移动。通过连续降低它们,你会得到一个自然失去动力的震动。如果您愿意,您可以调整这些数字。
【讨论】:
将其嵌入到 UIView 类别中的美观和合理的想法。 UIViews 应该动摇,期间! 当以较慢的速度(即较长的持续时间)使用时,此动画比使用 repeatCount 的另一个答案中的动画平滑得多。 使用这段代码后的一些想法。 定时功能 - 默认为线性,因此无需设置。 动画值 - 你可以更进一步,定义一个函数来产生很好的数学值......不过这也可以:)【参考方案4】:这是 swift 版本作为扩展,以防万一有人需要它
extension UIImageView
func vibrate()
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.05
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 2.0, self.center.y))
animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 2.0, self.center.y))
self.layer.addAnimation(animation, forKey: "position")
这将为一个小的 UIImageView(大约 15x15)设置动画。如果您需要制作更大的动画,您可能需要将 2.0 的运动因子更改为更大的动画。
【讨论】:
【参考方案5】:基于@bandejapaisa 的回答,Swift 3 的 UIView 扩展
extension UIView
func shake()
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = 0.6
animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]
layer.addAnimation(animation, forKey: "shake")
【讨论】:
【参考方案6】:你可以试试这段代码:
要调用下面的代码,请使用:[self earthquake:myObject];
#pragma mark EarthQuake Methods
- (void)earthquake:(UIView*)itemView
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
CGFloat t = 2.0;
CGAffineTransform leftQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, t, -t);
CGAffineTransform rightQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, t);
itemView.transform = leftQuake; // starting point
[UIView beginAnimations:@"earthquake" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:3];
[UIView setAnimationDuration:0.05];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(earthquakeEnded:finished:context:)];
itemView.transform = rightQuake; // end here & auto-reverse
[UIView commitAnimations];
- (void)earthquakeEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
if ([finished boolValue])
UIView* item = (UIView *)context;
item.transform = CGAffineTransformIdentity;
【讨论】:
【参考方案7】:你可以在 UIButton 点击事件上调用这个方法
-(void)shakescreen
//Shake screen
CGFloat t = 5.0;
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, t);
CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, -t);
self.view.transform = translateLeft;
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^
[UIView setAnimationRepeatCount:2.0];
self.view.transform = translateRight;
completion:^(BOOL finished)
if (finished)
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
self.view.transform = CGAffineTransformIdentity;
completion:NULL];
];
希望这会对你有所帮助:-)
【讨论】:
【参考方案8】:@imike 在 Swift 4.2 中的回答
extension UIView
func shake()
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.duration = 0.6
animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]
self.layer.add(animation, forKey: "shake")
【讨论】:
最后一行应该用 self.layer.add(animation, forKey: "shake") 修复【参考方案9】:answer how to create UIView shake animation in iOS 的 C# Xamarin.iOS 版本如下
CAKeyFrameAnimation keyframeAnimation = CAKeyFrameAnimation.GetFromKeyPath(new NSString("transform.translation.x"));
keyframeAnimation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut);
keyframeAnimation.Duration = 0.6f;
keyframeAnimation.Values = new NSObject[] new NSNumber(-20f), new NSNumber(20f), new NSNumber(-20f), new NSNumber(20f), new NSNumber(-10f), new NSNumber(10f), new NSNumber(-5f), new NSNumber(5f), new NSNumber(0f) ;
shakyView.Layer.AddAnimation(keyframeAnimation, "shake");
【讨论】:
【参考方案10】:这是一个使用阻尼器功能来衰减抖动的方法:
- (void)shake
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.duration = 0.5;
animation.delegate = self;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = YES;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
NSMutableArray* values = [[NSMutableArray alloc] init];
int steps = 100;
double position = 0;
float e = 2.71;
for (int t = 0; t < steps; t++)
position = 10 * pow(e, -0.022 * t) * sin(0.12 * t);
NSValue* value = [NSValue valueWithCGPoint:CGPointMake([self center].x - position, [self center].y)];
DDLogInfo(@"Value: %@", value);
[values addObject:value];
animation.values = values;
[[self layer] addAnimation:animation forKey:@"position"];
【讨论】:
【参考方案11】:我重构了@Matt Long 代码并将类别设为UIView
。现在它更易于重复使用和使用。
@implementation UIView (Animation)
- (void)shakeViewWithOffest:(CGFloat)offset
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
[animation setDuration:0.05];
[animation setRepeatCount:6];
[animation setAutoreverses:YES];
[animation setFromValue:@([self center].x-offset)];
[animation setToValue:@([self center].x+offset)];
[self.layer addAnimation:animation forKey:@"position.x"];
- (void)shake
[self shakeViewWithOffest:7.0f];
@end
【讨论】:
【参考方案12】:基于@Mihael-Isaev 答案的 Swift 3 实现
private enum Axis: StringLiteralType
case x = "x"
case y = "y"
extension UIView
private func shake(on axis: Axis)
let animation = CAKeyframeAnimation(keyPath: "transform.translation.\(axis.rawValue)")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = 0.6
animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]
layer.add(animation, forKey: "shake")
func shakeOnXAxis()
self.shake(on: .x)
func shakeOnYAxis()
self.shake(on: .y)
【讨论】:
【参考方案13】:斯威夫特 4.0:
基于最佳答案,但对动画进行了改进:这在动画的开始和结束时没有跳跃。
let midX = center.x
let midY = center.y
let rightAnim = CABasicAnimation(keyPath: #keyPath(CALayer.position))
rightAnim.duration = 0.07
rightAnim.autoreverses = true
rightAnim.fromValue = CGPoint(x: midX, y: midY)
rightAnim.toValue = CGPoint(x: midX + 9, y: midY)
let leftAnim = CABasicAnimation(keyPath: #keyPath(CALayer.position))
leftAnim.duration = 0.07
leftAnim.autoreverses = true
leftAnim.fromValue = CGPoint(x: midX, y: midY)
leftAnim.toValue = CGPoint(x: midX - 9, y: midY)
let group = CAAnimationGroup()
group.duration = leftAnim.duration + rightAnim.duration
group.animations = [rightAnim, leftAnim]
group.repeatCount = 3
layer.add(group, forKey: #keyPath(CALayer.position))
【讨论】:
【参考方案14】:你可以试试下面的代码:
+ (void)vibrateView:(UIView*)view
CABasicAnimation *shiverAnimationR;
shiverAnimationR = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
shiverAnimationR.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(1)];
//shiverAnimationR.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(-10)];
shiverAnimationR.duration = 0.1;
shiverAnimationR.repeatCount = 1000000.0; // Use A high Value
shiverAnimationR.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[view.layer addAnimation: shiverAnimationR forKey:@"shiverAnimationR"];
CABasicAnimation * shiverAnimationL;
shiverAnimationL = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//shiverAnimationL 2.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(10)];
shiverAnimationL.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(-1)];
shiverAnimationL.duration = 0.1;
shiverAnimationL.repeatCount = 1000000.0;
shiverAnimationL.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[view.layer addAnimation: shiverAnimationL forKey:@"shiverAnimationL"];
From the link.
【讨论】:
【参考方案15】:这是一个使用的版本,
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
在 iOS 7 中引入。
const CGFloat xDelta = 16.0f;
[UIView animateKeyframesWithDuration:0.50f
delay:0.0f
options:UIViewKeyframeAnimationOptionCalculationModeLinear
animations:^
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:(1.0/6.0)
animations:^
self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(xDelta, 0.0);
];
[UIView addKeyframeWithRelativeStartTime:(1.0/6.0)
relativeDuration:(1.0/6.0)
animations:^
self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(-xDelta, 0.0);
];
[UIView addKeyframeWithRelativeStartTime:(1.0/3.0)
relativeDuration:(1.0/3.0)
animations:^
self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(xDelta/2.0, 0.0);
];
[UIView addKeyframeWithRelativeStartTime:(2.0/3.0)
relativeDuration:(1.0/3.0)
animations:^
self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformIdentity;
];
completion:NULL];
【讨论】:
【参考方案16】:这是一个 UIView 扩展,提供了一个很棒的摇动动画:https://gist.github.com/mourad-brahim/cf0bfe9bec5f33a6ea66
在 cmets 上提供了 Swift5 更新。
【讨论】:
以上是关于UIView 摇动动画的主要内容,如果未能解决你的问题,请参考以下文章
当我将 UIView 动画放在不同类的 containerView 中时,为啥我的 UIView 动画不起作用?