Встряхните визуальный эффект на iPhone (НЕ встряхивая устройство) - PullRequest
44 голосов
/ 27 октября 2009

При входе в систему я бы предпочел не показывать предупреждение, оно слишком мимолетно. Отображение предупреждения, а затем отображение текста где-то на экране входа в систему выглядит как дублирование.

Так что я бы хотел, чтобы он графически потряс мой вид входа в систему, когда пользователь вводит неправильный идентификатор пользователя и пароль, как на экране входа в систему Mac.

Кто-нибудь знает, есть ли способ осуществить это, или есть какие-либо предложения для другого эффекта, который я мог бы использовать?

Ответы [ 11 ]

97 голосов
/ 21 февраля 2012

Я думаю, что это более эффективное решение:

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.

59 голосов
/ 11 мая 2011

Использование анимации UIKit на основе блоков iOS 4+ (в основном на основе ответа 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];
        }
    }];
}
40 голосов
/ 01 декабря 2009

Я видел некоторую анимацию колебания и изменил ее, чтобы встряхнуть изображение в пикселях вертикально и вниз:

- (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;
    }
}
8 голосов
/ 27 октября 2009

Вот учебник, который подробно описывает, как это сделать в Какао. Должно быть таким же для iPhone (или, по крайней мере, очень похоже).

http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/

6 голосов
/ 27 октября 2009

Простое изменение координаты X свойства center вашего вида может помочь. Если вы не делали никакой основной анимации, прежде чем она будет довольно простой.

Сначала запустите анимацию вправо, затем прослушайте, чтобы она закончилась, затем вернитесь влево и так далее. Сокращение времени, чтобы оно «выглядело правильно», может занять некоторое время.

- (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];
  }
}
4 голосов
/ 04 апреля 2012

Этот фрагмент 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

Надеюсь, это кому-нибудь поможет:)

2 голосов
/ 08 сентября 2014

В 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];
1 голос
/ 20 июня 2012

Я знаю, что на вопрос уже дан ответ, но так как я уже реализовал нечто подобное ранее, я чувствую, что добавить его не помешает:

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

Надеюсь, это поможет!

1 голос
/ 24 января 2011

очень легкая встряска категории для UIVoew

https://github.com/jonasschnelli/UIView-I7ShakeAnimation

0 голосов
/ 09 марта 2016

Решение, которое я использовал для ограничений, которые я установил в своей раскадровке. Без использования 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
    }
}

В моем случае анимация просто продолжалась, но через несколько секунд я запускаю свой view-контроллер, это также останавливает мой таймер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...