2012-04-24 13 views
28

¿cómo puedo agregar una animación a un UITextField para indicar una contraseña incorrecta exactamente como la de la aplicación de Facebook (en la pantalla de inicio de sesión) o el cuadro de inicio de sesión de Mac OS X?animando UITextField para indicar una contraseña incorrecta

gracias de antemano.

+2

http://stackoverflow.com/a/9371196/294884 – Fattie

+0

duplicado posible de [Shake efecto visual en el iPhone (NO sacudiendo t El dispositivo)] (http://stackoverflow.com/questions/1632364/shake-visual-effect-on-iphone-not-shaking-the-device) – stefreak

Respuesta

43

Algo así

-(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]; 
            }]; 
} 

por lo que necesita tres cosas más: Una dirección int que se pone a 1 antes de la sacudida que se llama un int sacude, que se establece en 0 antes de la sacudida se llama y una MAX_SHAKES constante que es tan grande como quieras. Espero que ayude.

EDIT:

llamada así:

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

interior archivo de cabecera añadir

int direction; 
int shakes; 
+1

no funcionó, el campo de texto se mueve solo hacia el lado derecho y se detiene Apliqué los tres valores que mencionaste. – JAHelia

+0

@Kai: ¿Qué marco se requiere para soportar la animación anterior? – Deviator

+0

@JAHelia ok parece como si 'shakes> = MAX_SHAKES' se evalúa como' SÍ'. Pruebe con el registro o la depuración si ese es el caso. Si estoy en lo cierto: ¿Por qué es cierto, es decir, cuáles son los valores de 'shakes' y' MAX_SHAKES'?Si no, tal vez la 'dirección' no cambie, sino que se mantenga '1'. Verifíquelo también al iniciar sesión o depurando. @ M.Sharjeel Just UIKit y CoreGraphics –

13

(Ene 16 de 2015) Actualización: (UIViewAnimationOptions ENUM) elenco está muy bien y UIViewAnimationOptionCurveEaseOut es 2 < < 16 por UIView.h bajo typedef NS_OPTIONS (NSUInteger, UIViewAnim ationOptions)

(31 ene 2013) modificaron de nuevo la respuesta de Kai incluir:

  1. retardo de borde de 0,01 s
  2. easeInOut
  3. reducir la duración de los batidos de cada moverá de 0,09 a 0,04
  4. aceleración hacia abajo movimiento por un pt cada 1 ciclo completo (derecha-izquierda-derecha)

Nota: si planea agitar dos controles (correo electrónico y contraseña) juntos, es posible que desee evitar el uso de variables estáticas o de clase para batidos y traducir. En cambio, inicialice y pase shake y traduzca como parámetros. Utilicé estadísticas, por lo que no se necesitan variables de clase.

-(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) // reduce 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; 
         } 
        }]; 
} 
+2

El '(enum UIViewAnimationOptions) UIViewAnimationCurveEaseInOut' es un error y se traducirá a' UIViewAnimationOptionLayoutSubviews'. Debería pasar 'UIViewAnimationOptionCurveEaseInOut' que es del tipo' UIViewAnimationOptions' esperado. –

+0

@GuillaumeAlgis Gracias. Todavía estoy obteniendo un "Incapaz de satisfacer restricciones simultáneamente". error cuando lo ejecuto en iPad. Funciona y funciona, solo con errores de consola. ¿Puedes mostrar a qué te refieres? ¿Querías sustituir esto? "opciones: (UIViewAnimationOptions) UIViewAnimationOptionCurveEaseInOut" Thx! –

+0

Quise reemplazar '(enum UIViewAnimationOptions) UIViewAnimationCurveEaseInOut' por' UIViewAnimationOptionCurveEaseInOut'. El error "No se puede cumplir simultáneamente restricciones" no está relacionado con esto, sino con el AutoLayout. –

3

Si usted vino aquí en busca de una respuesta MonoTouch, aquí es una traducción aproximada de Dickey's code:

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); 
    }); 
} 

bien junto el resto de sus métodos de extensiones y llaman así:

password.Shake(); 
+0

Supongo que "loginButton.Transform" debería ser "view.Transform"? – Lejdholt

+0

@Lejdholt lol gracias. –

2

Aquí está mi vuelta en ella:

@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 
2

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 termine el temblor.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]; 
} 
2

iOS iPhone textbox password window view shaking

creé un método categoría para UIView que se puede utilizar para sacudir cualquier elemento - por ejemplo, un UITextField - con la capacidad de recibir notificaciones después de que el temblor haya terminado. 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 
+0

solución genial ... gracias. – JAHelia

5

Basado en una respuesta anterior método tan 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:) 

    }) 

} 

Llamarlo:

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

Hay una biblioteca de Swift para animar el campo de texto en github here. Simplemente importe el archivo swift e impleméntelo como se indica a continuación

// 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 
9

Esta respuesta de Swift 2.0 no requiere recurrencia ni bucles. Sólo aprovecha CABasicAnimation mediante el refinado de this SO answer:

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") 
} 

Actualizado para la Swift 4:

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 = CGPoint(x: shakeView.center.x - xDelta, y: shakeView.center.y) 
    let from_value = NSValue(cgPoint: from_point) 

    let to_point = CGPoint(x: shakeView.center.x + xDelta, y: 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.add(shake, forKey: "position") 
} 
1

También puede hacerlo utilizando animación 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í se puede el cambio duration, repeatCount .Cambio al fromValue y toValue cambiará la distancia recorrida en el movimiento de la

0

Swift 3 y stack_view instaed 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) 
     }) 
    } 
1

Dado que la pregunta era acerca de Objective-C, y puesto que estoy usando Objective-C en mi proyecto, creo esta traducción de Objective-C de this previous Swift answer podría ser útil para otra persona:

- (void)shakeView:(UIView*)view 
{ 
    CABasicAnimation *shake = [CABasicAnimation animationWithKeyPath:@"position"]; 
    CGFloat xDelta = 5.0; 
    shake.duration = 0.15; 
    shake.repeatCount = 2; 
    shake.autoreverses = YES; 

    CGPoint fromPoint = CGPointMake(view.center.x - xDelta, view.center.y); 
    CGPoint toPoint = CGPointMake(view.center.x + xDelta, view.center.y); 

    shake.fromValue = [NSValue valueWithCGPoint:fromPoint]; 
    shake.toValue = [NSValue valueWithCGPoint:toPoint]; 
    shake.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 
    [view.layer addAnimation:shake forKey:@"position"]; 
} 
Cuestiones relacionadas