UIView встряхнуть анимацию
Я пытаюсь сделать встряску 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"];
}
14 ответов
Я написал этот пост. Для 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"];
Вам придется поиграть с параметрами продолжительности и repeatCount, а также с расстоянием x от центра в значениях from и to, но оно должно дать вам то, что вам нужно. Надеюсь, это поможет. Дайте знать, если у вас появятся вопросы.
---
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")
Я предпочитаю это решение с хорошим упругим поведением, идеально подходящее для анимации встряхивания неправильного пароля.
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)
}
}
Вот моя милая и простая на вид версия. Она имитирует дрожание, которое вы испытываете в 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 от текущей позиции вида. Положительные значения смещают вид вправо, а отрицательные значения влево. Последовательно понижая их, вы получаете вибрацию, которая естественно теряет импульс. Вы можете настроить эти цифры, если хотите.
Вот быстрая версия как расширение на тот случай, если это кому-нибудь понадобится
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 на нечто большее.
Основываясь на ответе @bandejapaisa, расширение UIView для Swift 3
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")
}
}
@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]
view.layer.add(animation, forKey: "shake")
}}
Вы можете попробовать этот кусок кода:
для вызова кода ниже используйте: [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;
}
}
Вы можете вызвать этот метод для события щелчка 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];
}
}];
}
Надеюсь, что это поможет вам:-)
C# Xamarin.iOS версия ответа о том, как создать анимацию встряски UIView в 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");
Реализация Swift 3 на основе ответа @Mihael-Isaev
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)
}
}
Swift 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))
Я изменил код @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
Вот тот, который использует функцию демпфера, чтобы уменьшить дрожание:
- (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"];
}
Вы можете попробовать следующий код:
+ (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"];
}
Вот версия, использующая,
+ (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];
Вот расширение UIView, обеспечивающее потрясающую анимацию встряхивания: https://gist.github.com/mourad-brahim/cf0bfe9bec5f33a6ea66
Обновление Swift5 предоставлено в комментариях.