2009-10-27 10 views
44

In caso di errore di accesso, preferirei evitare di mostrare un avviso, è troppo fugace. Mostrare l'avviso e quindi mostrare il testo da qualche parte nella schermata di accesso sembra una duplicazione.Scuotere l'effetto visivo su iPhone (NON scuotere il dispositivo)

Quindi mi piacerebbe che scuotesse graficamente la mia vista di login quando l'utente immette l'ID utente e la password sbagliati come fa la schermata di login Mac.

Qualcuno sa se c'è un modo per rimuoverlo o avere suggerimenti per un altro effetto che potrei usare?

+0

5 anni dopo .. http://stackoverflow.com/questions/24356051 – Fattie

risposta

97

credo che questo sia una soluzione più efficiente:

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: si crea

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

solo oggetto di animazione ed è tutto eseguito al livello CoreAnimation.

+0

Perfetto MA per il 2014, vedi ... http://stackoverflow.com/questions/24356051 !! – Fattie

+1

Vedi anche Swift: http://stackoverflow.com/a/27988876/1438339 –

6

Cambiare semplicemente la coordinata X della proprietà centrale della vista potrebbe fare il trucco. Se non hai fatto alcuna animazione di base prima che sia abbastanza semplice.

Innanzitutto, avviare un'animazione a destra, quindi attendere che finisca, quindi tornare indietro a sinistra e così via. Abbassare i tempi per "sentirsi bene" potrebbe richiedere del tempo.

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

È anche possibile creare un'animazione e applicarla al 'CALayer' della vista. Questo ti permetterebbe di usare, ad esempio, un 'CAKeyframeAnimation' che ti permetterebbe di mettere a punto l'animazione. Per un'animazione come questa, probabilmente avrai bisogno di quel livello di flessibilità. – Alex

+0

vedere la mia risposta qui sotto - utilizza una singola animazione CoreAnimation che viene automaticamente rimossa al termine dell'animazione ... non è necessario richiamare o più blocchi animati – nielsbot

40

avevo visto qualche animazione oscillazione e cambiato a stringere una vista t pixel verticale e downleft:

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

Utilizzo di animazioni UIKit basate su blocchi iOS 4+ (e liberamente basato sulla risposta di 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]; 
     } 
    }]; 
} 
+4

Questa dovrebbe essere la risposta più alta alla domanda, poiché tutte le risposte di cui sopra non funziona correttamente (senza modifiche o omettendo il parametro vista) con ARC attivato. Usa i blocchi! –

+0

sì, questa è la risposta migliore, potresti non voler controllare "finito", altrimenti potresti finire con una strana trasformazione – slf

+2

Puoi farlo con una sola animazione autoreversing, senza bisogno di blocchi di completamento. (Ho postato il codice qui sotto) – nielsbot

4

Questo snippet di categoria UIView ha funzionato per me. Sta usando 3 CABasingAnimazioni applicate al livello di vista.

#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 

spero che qualcuno helpes :)

1

So che la questione è già risolta, ma dal momento che ho già implementato qualcosa di simile in precedenza, mi sento non sarebbe male per aggiungerlo:

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

inoltre, dal momento che si desidera che l'agitazione per indicare che l'utente non è riuscito a eseguire il login, si potrebbe anche prendere in considerazione l'aggiunta di questa animazione che tinge lo schermo rosso mentre lo schermo trema:

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

Spero che questo aiuti!

0

Utilizzando Auto Layout, mi sono adattato risposta Chris Miles', ma NSLayoutConstraints animati come questo:

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

In iOS 7.0 o versione successiva, UIKit animazione keyframe è disponibile.

[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]; 
0

Una soluzione che ho usato per i vincoli che ho impostato nel mio storyboard. Senza usare 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 
    } 
} 

Nel mio caso l'animazione appena tenuto in corso, ma ho il mio pop viewcontroller dopo una durata di pochi secondi, questo si ferma il mio aswell timer.

Problemi correlati