Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

animating UITextField to indicate a wrong password

how can I add an animation to a UITextField to indicate wrong password exactly like the one in facebook app (at login screen) or the Mac OS X login box ?

thank you in advance.

like image 636
JAHelia Avatar asked Apr 24 '12 08:04

JAHelia


4 Answers

Something like that

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

So you need three more things: An int direction which is set to 1 before the shake is called an int shakes, which is set to 0 before the shake is called and a constant MAX_SHAKES which is as large as you like. Hope that helps.

EDIT:

call it like this:

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

inside header file add

int direction;
int shakes;
like image 138
Kai Huppmann Avatar answered Nov 10 '22 12:11

Kai Huppmann


(Jan 16 2015) Update: (enum UIViewAnimationOptions) cast is fine and UIViewAnimationOptionCurveEaseOut is 2 << 16 per UIView.h under typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions)

(Jan 31 2013) Further modified Kai's answer to include:

  1. edge delay of 0.01s
  2. easeInOut
  3. reduce duration of shakes every shake from 0.09 to 0.04
  4. throttle down movement by a pt every 1 complete loop (right-left-right)

Note: if you plan on shaking two controls (email and password) together you might want to avoid using class or static variables for shakes and translate. Instead, initialize and pass shake and translate as parameters. I used statics so no class variables needed.

-(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;
                         }
                     }];
}
like image 13
Dickey Singh Avatar answered Nov 10 '22 11:11

Dickey Singh


This Swift 2.0 answer requires no recursion and no loops. Just leverages CABasicAnimation by refining 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")
}

Updated for 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")
}
like image 9
Crashalot Avatar answered Nov 10 '22 12:11

Crashalot


Based on a previous answer as swift method ready to use :

func shakeTextField(textField: UITextField, numberOfShakes: Int, direction: CGFloat, maxShakes: Int) {

    let interval: TimeInterval = 0.03

    UIView.animate(withDuration: interval, animations: { () -> Void in
        textField.transform = CGAffineTransform(translationX: 5 * direction, y: 0)

        }, completion: { (aBool :Bool) -> Void in

            if (numberOfShakes >= maxShakes) {
                textField.transform = .identity
                textField.becomeFirstResponder()
                return
            }

            self.shakeTextField(textField: textField, numberOfShakes: numberOfShakes + 1, direction: direction * -1, maxShakes: maxShakes)
    })

}

To call it :

shakeTextField(aTextField,numberOfShakes:0, direction :1, maxShakes : 10)
like image 8
ant_one Avatar answered Nov 10 '22 11:11

ant_one