web-dev-qa-db-fra.com

L'animation UIView saute au début

Je lance ce code ...

[UIView animateWithDuration:0.2
                      delay:0.0
                    options:UIViewAnimationOptionCurveEaseOut
                 animations:^{

                     CGAffineTransform settingsTransform = CGAffineTransformMakeTranslation(self.settingsView.frame.size.width, 0);
                     CGAffineTransform speedTransform = CGAffineTransformMakeTranslation(-self.speedView.frame.size.width, 0);

                     self.settingsView.transform = settingsTransform;
                     self.speedView.transform = speedTransform;

                 } completion:nil];

Mais lorsqu'il exécute les vues sautent la moitié de la transformation dans la direction opposée avant de glisser jusqu'à la moitié d'une position dans la bonne direction.

J'ai ralenti la durée de l'animation à 5 secondes, mais le saut initial est instantané et la moitié de la transformation dans la mauvaise direction.

Quand j'anime en utilisant ce code ...

[UIView animateWithDuration:0.2
                      delay:0.0
                    options:UIViewAnimationOptionCurveEaseOut
                 animations:^{
                     self.settingsView.transform = CGAffineTransformIdentity;
                     self.speedView.transform = CGAffineTransformIdentity;
                 } completion:nil];

Cela fait exactement la même chose.

Le résultat est que le mouvement final correspond à la moitié de la transformation désirée puisqu'il saute la moitié de la transformation dans la mauvaise direction en premier.

Je ne peux vraiment pas comprendre pourquoi cela se produit?

Des idées.

::MODIFIER::

Éliminer une éventuelle ambiguïté.

Je n'essaie pas de faire en sorte que ces points de vue "rebondissent" là où ils se trouvent. Les vues que j'anime ressemblent au panneau de configuration situé au bord de l'écran. Lorsque l'utilisateur appuie sur "go", la vue glisse. Lorsque l'utilisateur appuie sur "stop", les panneaux reviennent dans la vue.

Au moins ils avaient l'habitude. Depuis l'activation de la mise en page automatique (dont j'ai besoin pour d'autres parties de l'application), je ne peux pas simplement changer le cadre des vues.

Vous pouvez voir cet effet en collant une vue dans un contrôleur de vue et un bouton permettant d'exécuter l'animation.

Merci

27
Fogmeister

OK, après avoir visionné à nouveau les vidéos WWDC, ils affirment que l'une des premières choses à faire lorsque vous utilisez AutoLayout est de supprimer tous les appels à setFrame.

En raison de la complexité de l'écran, j'ai complètement supprimé la mise en page automatique et j'utilise maintenant l'emplacement du cadre pour déplacer la vue sur l'écran.

Merci

8
Fogmeister

J'ai eu exactement le même problème, voici donc la solution que j'ai trouvée.

    CGAffineTransform transform = CGAffineTransformMake(1, 0, 0, 1, translation.x, translation.y);
    [UIView animateWithDuration:0.25 animations:^{
        _assetImageView.transform = transform;
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
    }];

J'appelle donc [self.view layoutIfNeeded]; à l'intérieur du bloc animé. Sans cela, le problème est identique à celui que vous avez et saute la traduction en distance puis s’anime ensuite à la position correcte. J'appelle cela depuis un contrôleur de vue, alors self est une sous-classe de UIViewController. Dans votre cas, "self.view" peut ne pas exister, mais j'espère que vous en comprendrez l'idée.

64
David Nesbitt

Aucune des solutions ici ne fonctionnait pour moi ... Mon animation sautait toujours. (sauf quand c'était juste à la fin d'une autre animation, un indice).

Quelques détails:

La vue qui faisait cela avait une série d'échelles et se traduisait déjà dans la pile.

Solution:  

Faire une animation par image clé, avec une première clé très courte qui réappliquerait la transformation que la dernière animation aurait dû définir.

    UIView.animateKeyframesWithDuration(0.4, delay: 0.0, options: [.CalculationModeCubic], animations: { () -> Void in
        //reset start point
        UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.01, animations: { () -> Void in
            self.element.transform = initialTransform
        })

        UIView.addKeyframeWithRelativeStartTime(0.01, relativeDuration: 0.99, animations: { () -> Void in
            self.element.transform = finalTransform
        })
    }, completion: nil)
15
Andres Canella

J'ai contacté le support technique Apple à propos de ce problème et obtenu cette réponse. Il s'agit donc d'un bogue.

iOS 8 présente actuellement un bogue connu dans les animations UIKit où Les animations de transformation obtiennent une valeur from from erronée (affectant principalement la position des objets animés, leur donnant l’impression de «sauter» inopinément au début d’une animation). 

[...] 

La solution de contournement jusqu'à ce qu'un correctif potentiel soit fourni doit être réduite à API de base d'animation pour coder vos animations.

7
denbec

Voici une version améliorée d'Andreas answer La différence essentielle est que vous ne pouvez spécifier qu'une seule image clé et obtenir moins de code

UIView.animateKeyframes(withDuration: animationDuration, delay: 0.0, options: [], animations: {
  UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1.0, animations: {
    animatedView.transform = newTransform
    })
  }, completion: nil)
2
bananabastard

Est-ce qu'une transformation a déjà été appliquée dans votre settingsView ou speedView avant que cette animation ait lieu?

Si les transformations de ces vues ne correspondent pas à la transformation d'identité (CGAffineTransformIdentity, autrement dit aucune transformation), vous ne pouvez pas accéder à leurs propriétés .frame.

Les propriétés de cadre d'UIViews ne sont pas valides lorsqu'une transformation leur est appliquée. Utilisez "bounds" à la place.

1
MikeS

Puisque vous voulez que votre deuxième animation se produise à partir de l'état actuel de votre première animation (qu'elle soit terminée ou non), je vous recommande d'utiliser l'option UIViewAnimationOptionLayoutSubviews lors du réglage de votre deuxième animation.

[UIView animateWithDuration:0.2
                      delay:0.0
                    options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionLayoutSubviews
                 animations:^{
                     self.settingsView.transform = CGAffineTransformIdentity;
                     self.speedView.transform = CGAffineTransformIdentity;
                 } completion:nil];

Voici un exemple du code utilisé pour tester en utilisant le modèle de contrôleur de vue simple:

myviewcontroller.m:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.animatedView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 160, 80)];
    self.animatedView.backgroundColor = [UIColor yellowColor];

    [UIView animateWithDuration:0.2
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionLayoutSubviews
                     animations:^{
                         CGAffineTransform settingsTransform = CGAffineTransformMakeTranslation(self.animatedView.frame.size.width, 0);

                         self.animatedView.transform = settingsTransform;

                     }
                     completion:nil];
    self.buttonToTest = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    self.buttonToTest.frame = CGRectMake(90, 20, 80, 40);
    [self.buttonToTest setTitle:@"Click Me!" forState:UIControlStateNormal];
    [self.buttonToTest addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];

    // set-up view hierarchy
    [self.view addSubview:self.buttonToTest];
    [self.view addSubview: self.animatedView];

}

- (void) buttonClicked
{
    [UIView animateWithDuration:.2
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionLayoutSubviews
                     animations:^{
                         self.animatedView.transform = CGAffineTransformIdentity;

                     }
                     completion:nil];
}
1
tiguero

J'ai eu ce problème et résolu par:

  1. Masquer la vue d'origine
  2. Ajouter une vue temporaire qui copie la vue que vous souhaitez animer
  3. Réaliser une animation qui se déroulera comme prévu
  4. Supprimer la vue temporaire et afficher la vue d'origine avec l'état final

J'espère que cela t'aides.

0
Gaz Long