web-dev-qa-db-fra.com

Comment animer une UIView avec des contraintes dans Swift?

Dans mon exemple artificiel, j'ai la vue unique suivante:

enter image description here

Comme vous pouvez le voir, il se compose de quelques contraintes simples:

  1. Aligner les centres horizontaux et verticaux,
  2. Hauteur (réglée sur une constante)
  3. Espaces de début et de fin (définis sur constants)

Ce que je cherche à réaliser, c'est que cette vue rougeâtre/rosâtre "entre" par le haut. Traditionnellement, dans un monde sans contraintes, je modifierais simplement le cadre à l'intérieur de UIView.animateWithDuration, mais je ne sais pas comment je fais la même chose dans un monde de contraintes.

Pour réitérer ma question, comment puis-je faire sortir ma vue de la scène et animer la vue en volant par le haut?

J'ai envisagé d'animer la contrainte de centres verticaux (et d'appeler par la suite layoutIfNeeded), mais elle n'atteint pas l'effet souhaité.

Merci de votre aide.

39
naivedeveloper

Ce que vous pouvez faire est d'ajouter le code suivant dans votre méthode viewDidAppear. Vous créez d'abord une propriété IBOutlet de la contrainte Y centrale de votre vue, puis vous modifiez sa valeur constante.

self.centerYConstraint.constant = 500.0
self.view.layoutIfNeeded()

UIView.animateWithDuration(Double(0.5), animations: {
        self.centerYConstraint.constant = 0
        self.view.layoutIfNeeded()
    })
66
gabbler

Ce que vous voulez est en fait assez simple, et je vais détailler comment cela doit être fait sans jouer avec les priorités ou les constantes funky. De cette façon, nous pouvons obtenir une animation qui fonctionnera sur n'importe quelle taille d'écran.

Le TL; DR est que vous devez configurer vos contraintes, puis appeler layoutIfNeeded à l'intérieur de votre bloc d'animation pour animer les modifications. Pour en savoir plus voir ce SO post .

Nous avons donc besoin de deux ensembles de contraintes pour masquer et afficher la vue. Dans viewDidLoad, la vue sera masquée, puis dans viewDidAppear ou là où nous voulons que cette vue se glisse.

Configuration du storyboard/XIB

Donc, la première chose à faire est de configurer les contraintes qui ne changeront pas dans IB (ou le code, selon ce qui vous convient). À savoir; la hauteur de la vue rouge et son espace avant et arrière vers le conteneur. Ainsi, vos contraintes pourraient ressembler à ceci ( note: Je n'ai pas défini de contrainte de largeur mais laissez les espaces de début et de fin définir la largeur):

Constraints

Vous verrez maintenant qu'IB vous avertira qu'aucune contrainte n'est configurée pour la position Y de votre vue (d'où les lignes pointillées rouges)

View with warnings

Vous pouvez maintenant ajouter l'espace supérieur à la contrainte de conteneur et le définir comme une contrainte d'espace réservé (case à cocher qui indique "supprimer au moment de la génération"). Nous le faisons parce que nous voulons contrôler où la vue se trouve par programme.

enter image description here

Cela signifie que cette contrainte n'existera pas une fois que la vue est chargée mais sert à supprimer tous vos avertissements car vous dites à IB que vous savez comment gérer cette contrainte lorsque la vue est chargée.

Temps de codage

Maintenant, nous avons besoin d'une méthode pour masquer la vue, une méthode pour afficher la vue. Pour cela nous allons stocker la contrainte de positionnement Y sur la vue puis l'animer. Notre viewController pourrait ressembler à ceci:

@IBOutlet weak var redView: UIView!
var redViewYPositionConstraint: NSLayoutConstraint?

override func viewDidLoad() {
    super.viewDidLoad()
    self.hideRedViewAnimated(false)
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.showRedViewAnimated(true)
}

Cache

Notre méthode pour masquer la vue peut simplement supprimer la contrainte de position, puis en ajouter une avec le bas des vues rouges égal au haut de la vue du contrôleur de vue:

func hideRedViewAnimated(animated: Bool) {
    //remove current constraint
    self.removeRedViewYPositionConstraint()
    let hideConstraint = NSLayoutConstraint(item: self.redView,
        attribute: .Bottom,
        relatedBy: .Equal,
        toItem: self.view,
        attribute: .Top,
        multiplier: 1,
        constant: 0)
    self.redViewYPositionConstraint = hideConstraint
    self.view.addConstraint(hideConstraint)
    //animate changes
    self.performConstraintLayout(animated: animated)
}

Montrant

De même, notre contrainte show déplace le centre Y de la vue vers le centre Y des contrôleurs:

func showRedViewAnimated(animated: Bool) {
    //remove current constraint
    self.removeRedViewYPositionConstraint()
    let centerYConstraint = NSLayoutConstraint(item: self.redView,
        attribute: .CenterY,
        relatedBy: .Equal,
        toItem: self.view,
        attribute: .CenterY,
        multiplier: 1,
        constant: 0)
    self.redViewYPositionConstraint = centerYConstraint
    self.view.addConstraint(centerYConstraint)
    //animate changes
    self.performConstraintLayout(animated: animated)
}

Méthodes pratiques

Pour être complet, les méthodes pratiques que j'ai utilisées ressemblent à ceci:

func performConstraintLayout(animated animated: Bool) {
    if animated == true {
          UIView.animateWithDuration(1,
          delay: 0,
          usingSpringWithDamping: 0.5,
          initialSpringVelocity: 0.6,
          options: .BeginFromCurrentState,
          animations: { () -> Void in
             self.view.layoutIfNeeded()
          }, completion: nil)
     } else {
          self.view.layoutIfNeeded()
     }
}

func removeRedViewYPositionConstraint() {
    if redViewYPositionConstraint != nil {
        self.view.removeConstraint(self.redViewYPositionConstraint!)
        self.redViewYPositionConstraint = nil
    }
}

Vous pouvez également vérifier si la vue rouge est visible en utilisant quelques calculs CGRect:

func isRedViewVisible() -> Bool {
    return CGRectContainsPoint(self.view.bounds, self.redView.frame.Origin)
}
12
Daniel Galasko

Essaye ça:

class ViewController: UIViewController {

    // red view
    @IBOutlet weak var myView: UIView!
    //  vertical align contraint
    @IBOutlet weak var verticalConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        verticalConstraint.constant = (myView.bounds.height + self.view.bounds.height)/2
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.view.layoutIfNeeded()
        UIView.animateWithDuration(0.5) {
            self.verticalConstraint.constant = 0
            self.view.layoutIfNeeded()
        }
    }

}

Faites attention à l'ordre de firstItem et secondItem dans la contrainte. Le code ci-dessus suppose que Superview est le firstItem:

screenshot


Une autre manière consiste à définir deux contraintes:

  • Contrainte d'alignement centre Y (Superview.CenterY == View.CenterY) priorité = 750
  • Contrainte d'espace vertical (SuperView.Top == View.Bottom) priorité = 500

screenshot

Et ajustez le priorité pour déterminer quelle contraint doit être adoptée.

class ViewController: UIViewController {

    //  vertical align contraint
    @IBOutlet weak var centerYConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        centerYConstraint.priority = 250
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.view.layoutIfNeeded()
        UIView.animateWithDuration(0.5) {
            self.centerYConstraint.priority = 750
            self.view.layoutIfNeeded()
        }
    }

}
9
rintaro

Pour tous ceux qui recherchent une animation simple comme la demande:

Pour l'animation vers le haut, vous devez utiliser [...] le CGAffineTransformMakeTranslation (x, y).

La raison en est que pour une animation de diapositive, vous devez d'abord déplacer la vue hors de l'écran, puis la ramener à sa position d'origine. Donc, dans votre viewDidLoad, utilisez simplement:

yourView.transform = CGAffineTransformMakeTranslation(0, 500)

Cela déplace la vue hors de l'écran, dans ce cas en bas. Maintenant dans le viewDidAppear vous pouvez montrer votre vue avec:

UIView.animateWithDuration(0.7, delay: 0.0, usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5, options: [], animations: {
        self.yourView.transform = CGAffineTransformMakeScale(1, 1)
    }, completion: nil)

Avec ces lignes, vous pouvez ajouter les contraintes souhaitées sur votre UIView et les placer là où vous en avez besoin dans votre ViewController.

6
Massimo Polimeni

Pour moi, la meilleure façon d'animer Swift 3 4

self.constraint.constant = -150

UIView.animate(withDuration: 0.45) { [weak self] in
    self?.view.layoutIfNeeded()
}
2
YannSteph