动画UITextField以指示错误的密码

问题描述 投票:28回答:12

如何将动画添加到UITextField以指示错误的密码,就像Facebook应用程序(登录屏幕)或Mac OS X登录框中的密码一样?

先感谢您。

iphone objective-c ios core-animation
12个回答
43
投票

这样的事情

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

所以你还需要三个东西:在摇动被称为int shakes之前设置为1的int方向,在调用抖动之前设置为0,并且是一个大小如你所愿的常量MAX_SHAKES。希望有所帮助。

编辑:

这样叫:

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

里面的头文件添加

int direction;
int shakes;

1
投票

由于问题是关于Objective-C,并且因为我在我的项目中使用Objective-C,我认为this previous Swift answer的这个Objective-C翻译可能对其他人有用:

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

0
投票

有一个Swift库可以在github here中为Textfield设置动画。只需导入swift文件并实现如下

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

0
投票

Swift 3和stackview代替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 )
        })
    }

13
投票

(2015年1月16日)更新:(枚举UIViewAnimationOptions)强制转换并且UIViewAnimationOptionCurveEaseOut在typedef NS_OPTIONS(NSUInteger,UIViewAnimationOptions)下每个UIView.h为2 << 16

(2013年1月31日)进一步修改Kai的答案包括:

  1. 边缘延迟0.01s
  2. easeInOut
  3. 减少每次震动的震动持续时间从0.09到0.04
  4. 每1个完整循环(右 - 右 - 右)通过pt减少运动

注意:如果您打算一起摇动两个控件(电子邮件和密码),您可能希望避免使用类或静态变量进行抖动和翻译。相反,初始化并传递抖动并作为参数进行转换。我使用静态因此不需要类变量。

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

9
投票

这个Swift 2.0答案不需要递归,也不需要循环。只需通过改进CABasicAnimation来利用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")
}

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

5
投票

基于以前的答案作为快速方法准备使用:

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: )

    })

}

打电话给它:

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

3
投票

如果你来这里寻找MonoTouch的答案,这里是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);
    });
}

把它与其余的扩展方法一起使用,然后调用:

password.Shake ();

2
投票

这是我的旋转:

@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
投票

我试过@stefreak解决方案,但循环方法在iOS 7.1上不起作用。所以我结合了@stefreak和@Chris的解决方案,并添加了完成块,以便在震动结束时收到通知。这是我的代码:

- (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
投票

我为UIView创建了一个可用于摇动任何元素的类别方法 - 例如UITextField - 能够在震动结束后收到通知。以下是如何使用它:

[myPasswordField shake];

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

这是代码。

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

1
投票

您也可以使用基本动画来完成

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

在这里你可以改变durationrepeatCount.Changing到fromValuetoValue将改变在摇动中移动的距离

© www.soinside.com 2019 - 2024. All rights reserved.