Встряхните визуальный эффект на iPhone (НЕ встряхивая устройство)
При неудаче входа в систему я бы предпочел не показывать предупреждение, это слишком быстро. Отображение предупреждения, а затем отображение текста где-то на экране входа в систему похоже на дублирование.
Поэтому я хотел бы, чтобы он графически встряхнул мой логин, когда пользователь вводит неверный идентификатор пользователя и пароль, как это делает экран входа в Mac.
Кто-нибудь знает, есть ли способ снять это или какие-либо предложения по другому эффекту, который я мог бы использовать?
Ответы
Ответ 1
Я думаю, что это более эффективное решение:
Swift:
let anim = CAKeyframeAnimation( keyPath:"transform" )
anim.values = [
NSValue( CATransform3D:CATransform3DMakeTranslation(-5, 0, 0 ) ),
NSValue( CATransform3D:CATransform3DMakeTranslation( 5, 0, 0 ) )
]
anim.autoreverses = true
anim.repeatCount = 2
anim.duration = 7/100
viewToShake.layer.addAnimation( anim, forKey:nil )
Obj-C:
CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:@"transform" ] ;
anim.values = @[
[ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-5.0f, 0.0f, 0.0f) ],
[ NSValue valueWithCATransform3D:CATransform3DMakeTranslation( 5.0f, 0.0f, 0.0f) ]
] ;
anim.autoreverses = YES ;
anim.repeatCount = 2.0f ;
anim.duration = 0.07f ;
[ viewToShake.layer addAnimation:anim forKey:nil ] ;
Создается только один объект анимации, и все это выполняется на уровне CoreAnimation.
Ответ 2
Использование iOS 4+ блочных анимаций UIKit (и свободно основанных на jayccrown ответе):
- (void)shakeView:(UIView *)viewToShake
{
CGFloat t = 2.0;
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, 0.0);
CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, 0.0);
viewToShake.transform = translateLeft;
[UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount:2.0];
viewToShake.transform = translateRight;
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
viewToShake.transform = CGAffineTransformIdentity;
} completion:NULL];
}
}];
}
Ответ 3
Я видел анимацию вобуляции и менял ее, чтобы встряхнуть вид t пикселей вертикально и вниз:
- (void)earthquake:(UIView*)itemView
{
CGFloat t = 2.0;
CGAffineTransform leftQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, t, -t);
CGAffineTransform rightQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, t);
itemView.transform = leftQuake; // starting point
[UIView beginAnimations:@"earthquake" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:5];
[UIView setAnimationDuration:0.07];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(earthquakeEnded:finished:context:)];
itemView.transform = rightQuake; // end here & auto-reverse
[UIView commitAnimations];
}
- (void)earthquakeEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
if ([finished boolValue])
{
UIView* item = (UIView *)context;
item.transform = CGAffineTransformIdentity;
}
}
Ответ 4
Вот учебник, в котором подробно описывается, как это сделать в Cocoa. Должно быть одинаковым для iPhone (или, по крайней мере, очень похожим).
http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/
Ответ 5
Простое изменение X-координаты свойства центра вашего представления может сделать трюк. Если вы еще не сделали какой-либо основной анимации, прежде чем сделать это довольно прямолинейно.
Сначала начните анимацию вправо, затем слушайте ее, чтобы закончить, а затем вернитесь влево и так далее. Получение времени вниз, чтобы он "чувствовал себя правильно", может занять некоторое время.
- (void)animationFinishCallback:(NSString *)animationID finished:(BOOL)finished context:(void *)context
{
if ([animationID isEqualToString:@"MoveRight"]) {
[UIView beginAnimations:@"MoveLeft" context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay: UIViewAnimationCurveEaseIn];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationFinishCallback:finished:context:)];
myView.center = CGRectMake(newX, newY);
[UIView commitAnimations];
}
}
Ответ 6
Этот фрагмент категории UIView работал у меня. Он использует 3 CABasingAnimations, применяемых для просмотра слоя.
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#define Y_OFFSET 2.0f
#define X_OFFSET 2.0f
#define ANGLE_OFFSET (M_PI_4*0.1f)
@interface UIView (shakeAnimation)
-(BOOL)isShakeAnimationRunning;
-(void)startShakeAnimation;
-(void)stopShakeAnimation;
@end
@implementation UIView (shakeAnimation)
-(BOOL)isShakeAnimationRunning{
return [self.layer animationForKey:@"shake_rotation"] != nil;
}
-(void)startShakeAnimation{
CFTimeInterval offset=(double)arc4random()/(double)RAND_MAX;
self.transform=CGAffineTransformRotate(self.transform, -ANGLE_OFFSET*0.5);
self.transform=CGAffineTransformTranslate(self.transform, -X_OFFSET*0.5f, -Y_OFFSET*0.5f);
CABasicAnimation *tAnim=[CABasicAnimation animationWithKeyPath:@"position.x"];
tAnim.repeatCount=HUGE_VALF;
tAnim.byValue=[NSNumber numberWithFloat:X_OFFSET];
tAnim.duration=0.07f;
tAnim.autoreverses=YES;
tAnim.timeOffset=offset;
[self.layer addAnimation:tAnim forKey:@"shake_translation_x"];
CABasicAnimation *tyAnim=[CABasicAnimation animationWithKeyPath:@"position.y"];
tyAnim.repeatCount=HUGE_VALF;
tyAnim.byValue=[NSNumber numberWithFloat:Y_OFFSET];
tyAnim.duration=0.06f;
tyAnim.autoreverses=YES;
tyAnim.timeOffset=offset;
[self.layer addAnimation:tyAnim forKey:@"shake_translation_y"];
CABasicAnimation *rAnim=[CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rAnim.repeatCount=HUGE_VALF;
rAnim.byValue=[NSNumber numberWithFloat:ANGLE_OFFSET];
rAnim.duration=0.15f;
rAnim.autoreverses=YES;
rAnim.timeOffset=offset;
[self.layer addAnimation:rAnim forKey:@"shake_rotation"];
}
-(void)stopShakeAnimation{
[self.layer removeAnimationForKey:@"shake_translation_x"];
[self.layer removeAnimationForKey:@"shake_translation_y"];
[self.layer removeAnimationForKey:@"shake_rotation"];
[UIView animateWithDuration:0.2f animations:^{
self.transform=CGAffineTransformRotate(self.transform, ANGLE_OFFSET*0.5);
self.transform=CGAffineTransformTranslate(self.transform, X_OFFSET*0.5, Y_OFFSET*0.5f);
}];
}
@end
Надеюсь, что это поможет кому-то:)
Ответ 7
В iOS 7.0 или новее доступна анимация ключевого кадра UIKit.
[UIView animateKeyframesWithDuration:0.5 delay:0.0 options:0 animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
NSInteger repeatCount = 8;
NSTimeInterval duration = 1.0 / (NSTimeInterval)repeatCount;
for (NSInteger i = 0; i < repeatCount; i++) {
[UIView addKeyframeWithRelativeStartTime:i * duration relativeDuration:duration animations:^{
CGFloat dx = 5.0;
if (i == repeatCount - 1) {
viewToShake.transform = CGAffineTransformIdentity;
} else if (i % 2) {
viewToShake.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, -dx, 0.0);
} else {
viewToShake.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, +dx, 0.0);
}
}];
}
} completion:completion];
Ответ 8
очень простая встряска для UIVoew
https://github.com/jonasschnelli/UIView-I7ShakeAnimation
Ответ 9
Я знаю, что на вопрос уже ответили, но поскольку я уже реализовал что-то подобное ранее, я чувствую, что это не помешает добавить его:
CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
NSArray *transformValues = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:((M_PI)/64)],
[NSNumber numberWithFloat:(-((M_PI)/64))],
[NSNumber numberWithFloat:((M_PI)/64)],
[NSNumber numberWithFloat:(-((M_PI)/64))],
[NSNumber numberWithFloat:((M_PI)/64)],
[NSNumber numberWithFloat:(-((M_PI)/64))],
[NSNumber numberWithFloat:0],
nil];
[shakeAnimation setValues:transformValues];
NSArray *times = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.14f],
[NSNumber numberWithFloat:0.28f],
[NSNumber numberWithFloat:0.42f],
[NSNumber numberWithFloat:0.57f],
[NSNumber numberWithFloat:0.71f],
[NSNumber numberWithFloat:0.85f],
[NSNumber numberWithFloat:1.0f],
nil];
[shakeAnimation setKeyTimes:times];
shakeAnimation.fillMode = kCAFillModeForwards;
shakeAnimation.removedOnCompletion = NO;
shakeAnimation.duration = 0.6f;
[self.viewToShake.layer addAnimation:shakeAnimation forKey:@"anim"];
Кроме того, поскольку вы хотите, чтобы тряска указывала на то, что пользователю не удалось войти в систему, вы также можете подумать о добавлении этой анимации, которая оттеняет красный экран, пока экран дрожит:
//Put this in the header (.h)
@property (nonatomic, strong) UIView *redView;
//Put this in the implementation (.m)
@synthesize redView;
//Put this in viewDidLoad
self.redView = [[UIView alloc] initWithFrame:self.view.frame];
self.redView.layer.opacity = 0.0f;
self.redView.layer.backgroundColor = [[UIColor redColor] CGColor];
//Put this wherever you check if the login failed
CAKeyframeAnimation *redTint = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
NSArray *transformValues = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.2f],
[NSNumber numberWithFloat:0.0f],
nil];
[redTint setValues:transformValues];
NSArray *times = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.5f],
[NSNumber numberWithFloat:1.0f],
nil];
[redTint setKeyTimes:times];
redTint.fillMode = kCAFillModeForwards;
redTint.removedOnCompletion = NO;
redTint.duration = 0.6f;
[self.redView.layer addAnimation:shakeAnimation forKey:@"anim"];
Надеюсь, это поможет!
Ответ 10
Используя автоматический макет, я адаптировал ответ Криса Майлса, но анимировал NSLayoutConstraints следующим образом:
NSLayoutConstraint *left = ...
NSLayoutConstraint *right = ...
[UIView animateWithDuration:0.08 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
[UIView setAnimationRepeatCount:3];
left.constant = 15.0;
right.constant = 25.0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.08 animations:^{
left.constant = 20.0;
right.constant = 20.0;
[self.view layoutIfNeeded];
} completion:NULL];
}
}];
Ответ 11
Решение, которое я использовал для ограничений, которые я установил в своем раскадровке. Без использования animateWithDuration.
@IBOutlet var balloonHorizontalConstraint: NSLayoutConstraint!
NSTimer.scheduledTimerWithTimeInterval(0.04, target: self, selector: "animateBalloon", userInfo: nil, repeats: true)
func animateBalloon() {
switch balloonHorizontalConstraint.constant {
case -46:
balloonHorizontalConstraint.constant = -50
default:
balloonHorizontalConstraint.constant = -46
}
}
В моем случае анимация просто продолжалась, но я поплю свой контроллер контроля по прошествии нескольких секунд, это также останавливает мой таймер.