52 votes

comment créer l'effet d'icône vacillante de l'iphone ?

Je veux faire osciller une image d'avant en arrière dans mon application, de la même manière que les icônes de l'iPhone oscillent lorsqu'on appuie dessus. Quelle est la meilleure façon de le faire ?

C'est ma première incursion dans les animations qui n'utilisent pas un GIF animé. Je pense que l'idée est de faire tourner légèrement l'image d'avant en arrière pour créer l'effet d'oscillation. J'ai envisagé d'utiliser CABasicAnimation et CAKeyframeAnimation. CABasicAnimation crée une instabilité à chaque répétition car il saute à la position de départ et n'interpole pas en retour. CAKeyframeAnimation semble être la solution, mais je n'arrive pas à la faire fonctionner. Il doit me manquer quelque chose. Voici mon code utilisant la CAKeyframeAnimation (qui ne fonctionne pas) :

    NSString *keypath = @"wobbleImage";
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
animation.duration = 1.0f;
animation.delegate = self;
animation.repeatCount = 5;

CGFloat wobbleAngle = 0.0872664626f;
NSValue *initial = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0f, 0.0f, 0.0f, 1.0f)];
NSValue *middle = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue *final = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:initial, middle, final, nil];

[imageView.layer addAnimation:animation forKey:keypath];

Il existe peut-être une solution plus simple qui m'échappe. J'apprécierais toute indication. Merci.

92voto

Pieter Points 9200

La réponse de Ramin était très bonne, mais depuis OS4, le même effet peut être obtenu en utilisant animateWithDuration dans une fonction simple également.

(adaptation de son exemple pour les futurs googlers)

#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)

- (void)startWobble {
 itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5));

 [UIView animateWithDuration:0.25 
      delay:0.0 
      options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
      animations:^ {
       itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5));
      }
      completion:NULL
 ];
}

- (void)stopWobble {
 [UIView animateWithDuration:0.25
      delay:0.0 
      options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear)
      animations:^ {
       itemView.transform = CGAffineTransformIdentity;
      }
      completion:NULL
  ];
}

0 votes

Cela a fonctionné pour moi, car je cherchais une solution basée sur des blocs.

0 votes

Je pense que c'est la façon la plus simple et la plus propre de le faire, merci !

0 votes

Comment redémarrer l'animation après avoir rejeté une vue modale ?

92voto

Ramin Points 10412

Une façon simple de le faire :

#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)

CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5.0));
CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5.0));

itemView.transform = leftWobble;  // starting point

[UIView beginAnimations:@"wobble" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:10];
[UIView setAnimationDuration:0.25];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(wobbleEnded:finished:context:)];

itemView.transform = rightWobble; // end here & auto-reverse

[UIView commitAnimations];

...

- (void) wobbleEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 
{
     if ([finished boolValue]) {
        UIView* item = (UIView *)context;
        item.transform = CGAffineTransformIdentity;
     }
}

Vous devrez probablement jouer avec le timing et les angles mais cela devrait vous aider à démarrer.

EDIT : J'ai modifié la réponse pour ajouter un code permettant de remettre l'objet dans son état d'origine une fois terminé. Notez également que vous pouvez utiliser l'option beginAnimations pour transmettre quoi que ce soit aux méthodes de démarrage et d'arrêt. Dans ce cas, il s'agit de l'objet d'oscillation lui-même, de sorte que vous n'avez pas à compter sur des ivars spécifiques et que la méthode peut être utilisée pour tout objet générique basé sur UIView (c'est-à-dire des étiquettes de texte, des images, etc.).

0 votes

cela fonctionne très bien, mais l'image se retrouve dans un état de rotation alors que je veux qu'elle se redresse dans l'état d'origine. savez-vous comment cela peut être fait dans le bloc d'animation lui-même ? dois-je utiliser le sélecteur animationDidStopSelector pour réinitialiser la position ?

0 votes

Joli ! Je ne l'ai pas essayé, mais on dirait que la vue commence brusquement avec une transformation tournée, et se termine brusquement d'une transformation tournée à la transformation d'identité (= aucune rotation). Donc vous aurez besoin de plus de code ici pour le faire correctement, c'est à dire faire 3 phases. phase 1) rotation animée vers leftWobble, phase 2) faire ce truc d'animation, phase 3) rotation animée vers la transformation d'identité.

0 votes

Merci ! Cela fonctionne très bien pour ce dont j'ai besoin. Pour une oscillation plus douce, les 3 phases semblent convenir, mais serait-il judicieux d'utiliser la CAKeyframeAnimation ?

14voto

docchang Points 682

Vous devez utiliser CAKeyframeAnimation pour rendre l'animation plus fluide.

+ (void) animationKeyFramed: (CALayer *) layer 
                   delegate: (id) object
              forKey: (NSString *) key {

    CAKeyframeAnimation *animation;
    animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.duration = 0.4;
    animation.cumulative = YES;
    animation.repeatCount = 2;
    animation.values = [NSArray arrayWithObjects:
            [NSNumber numberWithFloat: 0.0], 
            [NSNumber numberWithFloat: RADIANS(-9.0)], 
            [NSNumber numberWithFloat: 0.0],
            [NSNumber numberWithFloat: RADIANS(9.0)],
            [NSNumber numberWithFloat: 0.0], nil];
    animation.fillMode = kCAFillModeForwards;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    animation.removedOnCompletion = NO;
    animation.delegate = object;

    [layer addAnimation:animation forKey:key];
}

9voto

Kristopher Johnson Points 34554

J'ai écrit un exemple d'application qui tente de reproduire l'oscillation de l'écran d'accueil et le mouvement des icônes : Exemple de code pour l'iPhone : Carreaux

3 votes

J'apprécie que cet exemple de code comprenne également une translation dans la direction Y ; c'est un effet subtil qui manquait dans les solutions des autres.

1 votes

Une aide précieuse. Merci :)

2voto

Thanks Points 12709

Le moyen le plus simple que je connaisse est d'utiliser la Core Animation. En gros, vous créez un bloc Core Animation, puis vous effectuez une transformation et une configuration de la rotation et vous répétez le compte. Core Animation s'occupe alors de tout ce qui est nécessaire pour réaliser cet effet d'oscillation.

Pour démarrer un bloc de Core Animation, il suffit de faire :

[UIView beginAnimations:@"any string as animationID" context:self];
[UIView setAnimationRepeatCount:10];
// rotate 
[UIView commitAnimations];

non testé. Mais il se peut que vous ayez aussi à le faire :

[UIView setAnimationBeginsFromCurrentState:YES];

A.F.A.I.K. setAnimationRepeatCount aura pour effet que l'animation se fait, se défait, se fait, se défait, se fait, se défait, se fait... autant de fois que vous le spécifiez. Ainsi, vous pouvez d'abord faire une rotation vers la gauche sans compte de répétition, puis à partir de ce point, commencer à osciller avec un compte de répétition. Lorsque vous avez terminé, vous pouvez revenir à la transformation d'identité (= aucune rotation et mise à l'échelle appliquées).

Vous pouvez enchaîner les animations en définissant le délégué d'animation avec

[UIView setAnimationDelegate:self]

et ensuite

[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];

et dès que l'animation s'arrêtera, cette méthode sera appelée. Consultez la documentation de la classe UIView pour savoir comment implémenter cette méthode qui sera appelée lorsque l'animation s'arrêtera. En fait, à l'intérieur de cette méthode, vous effectuerez l'étape suivante (c'est-à-dire la rotation vers l'arrière, ou autre chose), avec un nouveau bloc d'animation mais avec le même contexte et le même ID d'animation, puis (si nécessaire) vous spécifierez un autre didStopSelector.

UPDATE :

Vous pourriez vouloir vérifier :

[UIView setAnimationRepeatAutoreverses:YES];

cela va osciller d'avant en arrière automatiquement.

0 votes

Merci ! Est-ce que je peux simplement mettre en place une chaîne de blocs d'animation l'un après l'autre dans ma fonction sans utiliser le DidStopSelector ? Y a-t-il des meilleures pratiques ou des considérations de performance ?

0 votes

Non. Je pense que vous devez utiliser le DidStopSelector. Malheureusement, cela semble peu pratique en code, car vous devrez créer une méthode pour chaque phase de l'animation. Mais vous pouvez les réutiliser si votre animation consiste en des phases simples comme "wobble to left", "wobble to center" et "wobble to right". Si vous n'aimez pas avoir plus d'une méthode pour cela, vous pouvez spécifier la méthode elle-même en tant que didStopSelector. Mais cela rendrait les choses plus compliquées. En général, je mets en place une série de méthodes les unes après les autres pour les enchaîner. Et lorsque vous avez terminé, mettez le didStopSelector à nil.

0 votes

Mis à jour : Voir [UIView setAnimationRepeatAutoreverses:YES] ;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X