Dans mon exemple artificiel, j'ai la vue unique suivante:
Comme vous pouvez le voir, il se compose de quelques contraintes simples:
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.
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()
})
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.
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):
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)
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.
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.
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)
}
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)
}
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)
}
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)
}
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
:
Une autre manière consiste à définir deux contraintes:
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()
}
}
}
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.
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()
}