2 votes

UIView animate - Rotation et mise à l'échelle, puis rotation et mise à l'échelle.

J'ai un aperçu d'image sous-classé que j'aimerais faire apparaître en fondu, mettre à l'échelle et faire tourner, puis continuer à tourner tout en réduisant l'échelle et en disparaissant en fondu.

J'utilise un bloc UIView animate avec un gestionnaire d'achèvement pour gérer le rétrécissement.

Le problème est que ce n'est pas une animation fluide. Avant que le gestionnaire d'achèvement ne s'exécute, l'animation s'arrête avant de se relancer. J'ai besoin d'une belle animation en un seul mouvement.

Code ci-dessous :

    let duration: TimeInterval = 3.0
    let rotate = CGAffineTransform(rotationAngle: CGFloat(Double.pi))

    UIView.animate(withDuration: duration * 3, delay: 0, options: [.curveLinear], animations: {
        // initial transform
        self.alpha = 0
        self.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
        // initial spin for duration of animaton
        UIView.animate(withDuration: duration * 3, delay: 0.0, options: [.curveLinear], animations: {
            self.transform = rotate
        }, completion: nil)

        // scaling and fading
        UIView.animate(withDuration: duration, delay: 0, options: [.curveLinear], animations: {
            UIView.setAnimationRepeatCount(3)
            self.transform = self.transform.scaledBy(x: 0.8, y: 0.8)
            self.alpha = 1
        }) { (true) in
            UIView.animate(withDuration: duration, animations: {
                UIView.setAnimationRepeatCount(3)
                self.transform = self.transform.scaledBy(x: 0.1, y: 0.1)
                self.alpha = 0
            })
        }
    }, completion: nil)

Comment faire pour que l'animation tourne pendant tout ce temps tout en faisant un fondu et en augmentant l'échelle avant de la réduire et de la faire disparaître ? L'animation entière doit durer 3 secondes et se répéter 3 fois. Merci.

4voto

DannyJi Points 11

enter image description here

Je vois le léger bégaiement dans la rotation au début de l'onCompletion.

J'ai créé une réduction avec votre code (illustré ci-dessous dans la vue bleue) et une variation dans la vue orange. Celle-ci a été prise dans le simulateur et transformée en GIF animé, la vitesse est donc ralentie. La vue orange continue de tourner alors que la transformation complète est simplement réduite.

Voici le code de la fonction layoutSubviews() pour la vue Orange

override func layoutSubviews() { super.layoutSubviews()

let duration: TimeInterval = 3.0
let rotate = CGAffineTransform(rotationAngle: CGFloat(Double.pi))

// initial transform
self.alpha = 0
self.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)

// initial spin for duration of animaton
UIView.animate(withDuration: duration,
               delay: 0.0,
               options: [.curveLinear],
               animations: {
                  self.transform = rotate;
                },
               completion: nil)
// scaling and fading
UIView.animate(withDuration: duration/2.0, animations: {
  self.transform = self.transform.scaledBy(x: 0.8, y: 0.8)
  self.alpha = 1
}) { (true) in
  UIView.animate(withDuration: duration/2.0, animations: {
    self.transform = self.transform.scaledBy(x: 0.1, y: 0.1)
    self.alpha = 0
  })
}

}

4voto

DannyJi Points 11

Pour votre demande modifiée, je développe ce que vous avez déjà vu en utilisant les animations de blocs. Comme certains l'ont dit, les animations par images clés sont peut-être meilleures, mais voici ce que je pense.

Créez une animation qui tourne tout le temps en transformant la vue. Créez une autre animation qui effectue la mise à l'échelle et le fondu en fonction de la transformation actuelle (qui est la rotation). Dans ce passage, j'ai juste créé quelques variables pour vous permettre de personnaliser (et répéter) des portions de l'animation. J'ai cassé certaines choses pour être clair et je sais que je pourrais refactorer pour écrire quelque chose d'encore plus concis.

Voici le code

import UIKit

class OrangeView: UIView {

override func layoutSubviews() {
    super.layoutSubviews()

  let duration: TimeInterval = 9.0

  self.transform = CGAffineTransform.identity

  // initial transform
  self.alpha = 1
  self.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)

  // start rotation
  rotate(duration: duration)

  // scaling and fading
  scaleUpAndDown(desiredRepetitions: 3, initalDuration: duration)

  }

func rotate(duration: TimeInterval) {
                UIView.animate(withDuration: duration/2.0,
                               delay: 0.0,
                               options: [.curveLinear], animations: {
                                let angle = Double.pi
                                self.transform = self.transform.rotated(by: CGFloat(angle))
                }, completion: {[weak self] finished in
  guard let strongSelf = self else {
    return
  }
  if finished &&
    strongSelf.transform != CGAffineTransform.identity {
    strongSelf.rotate(duration: duration)
  } else {
    // rotation ending
  }
})

  }

func scaleUpAndDown(timesRepeated: Int = 0, desiredRepetitions: Int, initalDuration: TimeInterval) {

guard timesRepeated < desiredRepetitions,
  desiredRepetitions > 0, initalDuration > 0 else {
  self.transform = CGAffineTransform.identity
    return
}
let repeatedCount = timesRepeated + 1

let scalingDuration = initalDuration/2.0/Double(desiredRepetitions)

UIView.animate(withDuration: scalingDuration,
               delay: 0.0,
               animations: {
                let desiredOriginalScale: CGFloat = 0.8

                let scaleX = abs(CGAffineTransform.identity.a / self.transform.a) * desiredOriginalScale
                let scaleY = abs(CGAffineTransform.identity.d / self.transform.d) * desiredOriginalScale

                self.transform = self.transform.scaledBy(x: scaleX, y: scaleY)
                self.alpha = 1
              }) { (true) in
                UIView.animate(withDuration:scalingDuration,
                               delay: 0.0,
                               animations: {

                                let desiredOriginalScale: CGFloat = 0.1

                                let scaleX = abs(CGAffineTransform.identity.a / self.transform.a) * desiredOriginalScale
                                let scaleY = abs(CGAffineTransform.identity.d / self.transform.d) * desiredOriginalScale
                                  self.transform = self.transform.scaledBy(x: scaleX, y: scaleY)
                                  self.alpha = 0
                }) { finshed in
                  self.scaleUpAndDown(timesRepeated: repeatedCount, desiredRepetitions: desiredRepetitions, initalDuration: initalDuration);
                }
              }

  }

}

Enfin, voici un autre gif animé

enter image description here

1voto

whyp Points 453

Essayez d'utiliser CGAffineTransformConcat().

  CGAffineTransform scale = CGAffineTransformMakeScale(0.8, 0.8);
  self.transform = CGAffineTransformConcat(CGAffineTransformRotate(self.transform, M_PI / 2), scale);

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