animando UITextField para indicar una contraseña incorrecta

¿Cómo puedo agregar una animation a un UITextField para indicar una contraseña incorrecta exactamente igual a la que UITextField en la aplicación de Facebook (en la pantalla de inicio de session) o en el recuadro de inicio de session de Mac OS X?

gracias de antemano.

Algo como eso

 -(void)shake:(UIView *)theOneYouWannaShake { [UIView animateWithDuration:0.03 animations:^ { theOneYouWannaShake.transform = CGAffineTransformMakeTranslation(5*direction, 0); } completion:^(BOOL finished) { if(shakes >= 10) { theOneYouWannaShake.transform = CGAffineTransformIdentity; return; } shakes++; direction = direction * -1; [self shake:theOneYouWannaShake]; }]; } 

Entonces necesitas tres cosas más: una dirección int que se establece en 1 antes del batido se llama batidos int, que se establece en 0 antes de que se invoque el batido y una MAX_SHAKES constante que sea tan grande como quieras. Espero que ayude.

EDITAR:

llámalo así:

  direction = 1; shakes = 0; [self shake:aUIView]; 

dentro del file de encabezado agregar

 int direction; int shakes; 

(16 de enero de 2015) Actualización: el enunciado UIViewAnimationOptions está bien y UIViewAnimationOptionCurveEaseOut es 2 << 16 por UIView.h en typedef NS_OPTIONS (NSUInteger, UIViewAnimationOptions)

(31 de enero de 2013) Otra respuesta de Kai modificada para include:

  1. retraso de borde de 0.01 s
  2. easeInOut
  3. networkingucir la duración de los batidos cada batido de 0.09 a 0.04
  4. Acelere el movimiento por una pt cada 1 bucle completo (derecha-izquierda-derecha)

Nota: si planea sacudir dos controles (correo electrónico y contraseña) juntos, es posible que desee evitar el uso de variables de class o estáticas para mezclar y traducir. En su lugar, inicialice y pase el movimiento y traduzca como parameters. Utilicé la estática para que no se necesitaran las variables de class.

 -(void)shakeAnimation:(UIView*) view { const int reset = 5; const int maxShakes = 6; //pass these as variables instead of statics or class variables if shaking two controls simultaneously static int shakes = 0; static int translate = reset; [UIView animateWithDuration:0.09-(shakes*.01) // networkinguce duration every shake from .09 to .04 delay:0.01f//edge wait delay options:(enum UIViewAnimationOptions) UIViewAnimationCurveEaseInOut animations:^{view.transform = CGAffineTransformMakeTranslation(translate, 0);} completion:^(BOOL finished){ if(shakes < maxShakes){ shakes++; //throttle down movement if (translate>0) translate--; //change direction translate*=-1; [self shakeAnimation:view]; } else { view.transform = CGAffineTransformIdentity; shakes = 0;//ready for next time translate = reset;//ready for next time return; } }]; } 

Esta respuesta Swift 2.0 no requiere recursion ni loops. Simplemente aprovecha CABasicAnimation refinando esta respuesta SO :

 func shakeView(shakeView: UIView) { let shake = CABasicAnimation(keyPath: "position") let xDelta = CGFloat(5) shake.duration = 0.15 shake.repeatCount = 2 shake.autoreverses = true let from_point = CGPointMake(shakeView.center.x - xDelta, shakeView.center.y) let from_value = NSValue(CGPoint: from_point) let to_point = CGPointMake(shakeView.center.x + xDelta, shakeView.center.y) let to_value = NSValue(CGPoint: to_point) shake.fromValue = from_value shake.toValue = to_value shake.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) shakeView.layer.addAnimation(shake, forKey: "position") } 

Basado en una respuesta previa como método rápido listo para usar:

 func shakeTextField (textField : UITextField, numberOfShakes : Int, direction: CGFloat, maxShakes : Int) { let interval : NSTimeInterval = 0.03 UIView.animateWithDuration(interval, animations: { () -> Void in textField.transform = CGAffineTransformMakeTranslation(5 * direction, 0) }, completion: { (aBool :Bool) -> Void in if (numberOfShakes >= maxShakes) { textField.transform = CGAffineTransformIdentity textField.becomeFirstResponder() return } self.shakeTextField(textField, numberOfShakes: numberOfShakes + 1, direction: direction * -1, maxShakes: ) }) } 

Para llamarlo:

 shakeTextField(aTextField,numberOfShakes:0, direction :1, maxShakes : 10) 

Si viniste aquí buscando una respuesta de MonoTouch, aquí hay una traducción aproximada del código de Dickey :

 public static void /*Harlem*/Shake (this UIView view, int shakes = 6, int translation = 5) { UIView.Animate (0.03 + (shakes * 0.01), 0.01, UIViewAnimationOptions.CurveEaseInOut, () => { view.Transform = CGAffineTransform.MakeTranslation (translation, 0); }, () => { if (shakes == 0) { view.Transform = CGAffineTransform.MakeIdentity (); return; } if (translation > 0) translation --; translation *= -1; shakes --; Shake (view, shakes, translation); }); } 

Póngalo con el rest de sus methods de extensiones y llame así:

 password.Shake (); 

Aquí está mi giro:

 @implementation UITextField (Shake) - (void)shake { [self shakeWithIterations:0 direction:1 size:4]; } #pragma mark - Private - (void)shakeWithIterations:(int)iterations direction:(int)direction size:(int)size { [UIView animateWithDuration:0.09-(iterations*.01) animations:^{ self.transform = CGAffineTransformMakeTranslation(size*direction, 0); } completion:^(BOOL finished) { if (iterations >= 5 || size <= 0) { self.transform = CGAffineTransformIdentity; return; } [self shakeWithIterations:iterations+1 direction:direction*-1 size:MAX(0, size-1)]; }]; } @end 

Probé la solución @stefreak, pero el enfoque de bucle no funciona en iOS 7.1. Así que combiné las soluciones de @stefreak y @Chris, y agregué el bloque de finalización para recibir una notificación cuando el temblor termina. Aquí está mi código:

 - (void)shakeView:(UIView *)view iterations:(NSInteger)iterations direction:(NSInteger)direction completion:(void (^)())completion { const NSInteger MAX_SHAKES = 6; const CGFloat SHAKE_DURATION = 0.05; const CGFloat SHAKE_TRANSFORM = 10.0; [UIView animateWithDuration:SHAKE_DURATION delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{ view.transform = iterations >= MAX_SHAKES ? CGAffineTransformIdentity : CGAffineTransformMakeTranslation(SHAKE_TRANSFORM * direction, 0); } completion:^(BOOL finished) { if (finished) { if (iterations >= MAX_SHAKES) { if (completion) { completion(); } } else { [self shakeView:view iterations:(iterations + 1) direction:(direction * -1) completion:completion]; } } }]; } - (void)shakeView:(UIView *)view completion:(void (^)())completion { [self shakeView:view iterations:0 direction:1 completion:completion]; } 

Vista de la ventana de contraseña del cuadro de texto iOS iPhone iPhone sacudiendo

Creé un método de categoría para UIView que se puede utilizar para sacudir cualquier elemento, por ejemplo, un UITextField, con la capacidad de recibir una notificación después de que el temblor haya finalizado. Aquí es cómo usarlo:

 [myPasswordField shake]; // Or with a callback after the shake [myPasswordField shakeWithCallback:^{ NSLog(@"Shaking has ended"); }]; 

Aquí está el código.

UIView + Shake.h

 #import <UIKit/UIKit.h> @interface UIView (UIView_Shake) -(void)shake; -(void)shakeWithCallback:(void (^)(void))completeBlock; @end 

UIView + Shake.m

 #import "UIView+Shake.h" #import <objc/runtime.h> @implementation UIView (UIView_Shake) static void *NumCurrentShakesKey; static void *NumTotalShakesKey; static void *ShakeDirectionKey; - (int)numCurrentShakes { return [objc_getAssociatedObject(self, &NumCurrentShakesKey) intValue]; } - (void)setNumCurrentShakes:(int)value { objc_setAssociatedObject(self, &NumCurrentShakesKey, [NSNumber numberWithInt:value], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (int)numTotalShakes { return [objc_getAssociatedObject(self, &NumTotalShakesKey) intValue]; } - (void)setNumTotalShakes:(int)value { objc_setAssociatedObject(self, &NumTotalShakesKey, [NSNumber numberWithInt:value], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (int)shakeDirection { return [objc_getAssociatedObject(self, &ShakeDirectionKey) intValue]; } - (void)setShakeDirection:(int)value { objc_setAssociatedObject(self, &ShakeDirectionKey, [NSNumber numberWithInt:value], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(void)shake { [self shakeNextWithCompleteBlock:nil]; } -(void)shakeWithCallback:(void (^)(void))completeBlock { self.numCurrentShakes = 0; self.numTotalShakes = 6; self.shakeDirection = 8; [self shakeNextWithCompleteBlock:completeBlock]; } -(void)shakeNextWithCompleteBlock:(void (^)(void))completeBlock { UIView* viewToShake = self; [UIView animateWithDuration:0.08 animations:^ { viewToShake.transform = CGAffineTransformMakeTranslation(self.shakeDirection, 0); } completion:^(BOOL finished) { if(self.numCurrentShakes >= self.numTotalShakes) { viewToShake.transform = CGAffineTransformIdentity; if(completeBlock != nil) { completeBlock(); } return; } self.numCurrentShakes++; self.shakeDirection = self.shakeDirection * -1; [self shakeNextWithCompleteBlock:completeBlock]; }]; } @end 

También puedes hacerlo usando animation básica

 let animation = CABasicAnimation(keyPath: "position") animation.duration = 0.09 animation.repeatCount = 4 animation.autoreverses = true animation.fromValue = NSValue(CGPoint: CGPointMake(txtField.center.x - 10, txtField.center.y)) animation.toValue = NSValue(CGPoint: CGPointMake(txtField.center.x + 10, txtField.center.y)) txtField.layer.addAnimation(animation, forKey: "position") 

Aquí puede cambiar la duration , repeatCount Cambiar a fromValue y toValue cambiará la distancia recorrida en el movimiento.

Hay una biblioteca Swift para animar Textfield en github aquí . Simplemente importe el file swift e impleméntelo de la siguiente manera.

 // Shake with the default speed self.textField.shake(10, delta:5) //10 no. of shakes with 5 points wide // Shake with a custom speed self.sampleText.shake(10, delta: 5, speed: 0.10) //10 no. of shakes with 5 points wide in 100ms per shake 

Swift 3 y stack_view instalan textField

 func shakeTextField (stack_view : UIStackView, numberOfShakes : Int, direction: CGFloat, maxShakes : Int) { let interval : TimeInterval = 0.05 UIView.animate(withDuration: interval, animations: { () -> Void in stack_view.transform = CGAffineTransform(translationX: 5 * direction, y: 0) }, completion: { (aBool :Bool) -> Void in if (numberOfShakes >= maxShakes) { stack_view.becomeFirstResponder() return } self.shakeTextField(stack_view: stack_view, numberOfShakes: numberOfShakes + 1, direction: direction * -1, maxShakes: maxShakes ) }) }