动画 UITextField 以指示错误的密码
Posted
技术标签:
【中文标题】动画 UITextField 以指示错误的密码【英文标题】:animating UITextField to indicate a wrong password 【发布时间】:2012-04-24 08:37:31 【问题描述】:如何向UITextField
添加动画以指示密码错误,与 facebook 应用程序(登录屏幕)或 Mac OS X 登录框中的密码完全相同?
提前谢谢你。
【问题讨论】:
***.com/a/9371196/294884 Shake visual effect on iPhone (NOT shaking the device)的可能重复 【参考方案1】:类似的东西
-(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 方向,在摇动之前设置为 1 称为 int 摇动,它在调用摇动之前设置为 0,以及一个常量 MAX_SHAKES,它与你喜欢的一样大。希望对您有所帮助。
编辑:
这样称呼它:
direction = 1;
shakes = 0;
[self shake:aUIView];
头文件内添加
int direction;
int shakes;
【讨论】:
它不起作用,文本字段仅移动到右侧并停止。我已经应用了你提到的所有三个值。 @Kai:上面的动画需要哪个框架支持? @JAHelia ok 看起来好像shakes >= MAX_SHAKES
评估为 YES
。如果是这种情况,请使用日志记录或调试进行测试。如果我是对的:为什么是真的,即shakes
和MAX_SHAKES
的值是什么?如果不是,direction
可能不会改变,但会保持1
。也可以通过记录或调试来检查。 @M.Sharjeel 只是 UIKit 和 CoreGraphics
@Kai,非常感谢你,在你更新你的代码之后它已经工作了(我当然在我第一次运行应用程序时修正了错字)。无论如何,有没有可能让动画更流畅一点,如果你看一下Mac OSX Lion的登录框并输入错误的密码你会发现动画弹跳比我们这里的情况更流畅。
@JAHelia 正如我所说,我测试并更新了代码中的值。对我来说它现在看起来很好,但也许你想放慢一点(改变持续时间参数)或通过改变 CGAffineTransformMakeTranslation(5*direction, 0) 中的 5 来扩大/缩小每个方向的方式。如果你想平滑所有你也可以用不同的动画曲线进行测试,那么你必须更改为: UIView animateWithDuration:0.05 delay:.0 options: UIViewAnimationCurveEaseInOut animations: ... completion: ...];【参考方案2】:
(2015 年 1 月 16 日)更新: (enum UIViewAnimationOptions) cast 很好,并且 UIViewAnimationOptionCurveEaseOut 是 2
(2013 年 1 月 31 日) 进一步修改凯的答案包括:
-
0.01s 边沿延迟
缓入输出
将每次摇晃的持续时间从 0.09 减少到 0.04
每 1 个完整循环(右-左-右)将运动降低 1 个百分点
注意:如果您计划同时摇动两个控件(电子邮件和密码),您可能希望避免使用类或静态变量进行摇动和翻译。相反,初始化并传递shake 和translate 作为参数。我使用了静态,所以不需要类变量。
-(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;
];
【讨论】:
(enum UIViewAnimationOptions) UIViewAnimationCurveEaseInOut
是一个错误,将被翻译成UIViewAnimationOptionLayoutSubviews
。您应该传递UIViewAnimationOptionCurveEaseInOut
,它是预期的UIViewAnimationOptions
类型。
@GuillaumeAlgis 谢谢。我仍然收到“无法同时满足约束”。在 iPad 上运行时出错。它确实可以工作并运行,只是出现控制台错误。你能说明你的意思吗?你的意思是用这个代替吗? "选项:(UIViewAnimationOptions) UIViewAnimationOptionCurveEaseInOut" 谢谢!
我的意思是用UIViewAnimationOptionCurveEaseInOut
替换(enum UIViewAnimationOptions) UIViewAnimationCurveEaseInOut
。 “无法同时满足约束”错误与此无关,而是与 AutoLayout 相关。【参考方案3】:
这个 Swift 2.0 答案不需要递归和循环。只需通过精炼this SO answer 来利用CABasicAnimation
:
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")
【讨论】:
【参考方案4】:基于先前的答案,作为可以使用的快速方法:
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)
)
叫它:
shakeTextField(aTextField,numberOfShakes:0, direction :1, maxShakes : 10)
【讨论】:
【参考方案5】:如果您来这里寻找 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 ();
【讨论】:
我猜“loginButton.Transform”应该是“view.Transform”?【参考方案6】:我为 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
【讨论】:
【参考方案7】:这是我的看法:
@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
【讨论】:
【参考方案8】:我尝试了@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];
【讨论】:
【参考方案9】:您也可以使用基本动画来做到这一点
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")
您可以在此处更改duration
,repeatCount
。更改为fromValue
和toValue
将更改在摇晃中移动的距离
【讨论】:
【参考方案10】:由于问题是关于 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"];
【讨论】:
【参考方案11】:在 github here 中有一个用于动画 Textfield 的 Swift 库。只需导入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
【讨论】:
【参考方案12】:Swift 3 和 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 )
)
【讨论】:
以上是关于动画 UITextField 以指示错误的密码的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 UI Button 正常工作,但没有动画或向用户指示?