[CoreAnimation] Animation de layer qui ne fonctionne pas avec animation de contrainte.

PyrohPyroh Membre
novembre 2014 modifié dans Dev. macOS #1
Bonjour à  tous.
Je joue actuellement avec autolayout et les animations. Pour faire simple j'ai repris l'exemple d'Apple (InfoBarStackView) qui montre comment faire des vue qui s'escamotent quand on appuie sur un bouton (comme dans tous les inspector d'OSX). Le résultat et bon :



J'ai voulu pousser le vice plus loin en "écrasant" ou "gonflant" le contenu de la view en animant le layer.
Là  encore pas de réel problème :
 


Par contre les 2 en même temps me donnent un résultat inattendu.
 


Séparement tout se passe bien, ensemble y'a un soucis sur le "shrink" mais le "grow" fonctionne...
Voilà  le code qui s'occupe de ça (en Swift) :
@IBAction func toggle(sender: AnyObject) {
if !disclosed {
let trans = CATransform3DMakeScale(1.0, 0.001, 1.0) //0.0 donne une erreur
func shrink(ctx: NSAnimationContext!) {
ctx.duration = self.ANIM_DURATION
ctx.allowsImplicitAnimation = true
ctx.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

self.closeConstraint.animator().constant = 0
self.handledViewController.view.layer?.transform = trans
self.toggleButton.animator().title = "Show"
disclosed = true
}
NSAnimationContext.runAnimationGroup(shrink, completionHandler: {})
} else {
func grow(ctx: NSAnimationContext!) {
ctx.duration = self.ANIM_DURATION
ctx.allowsImplicitAnimation = true
ctx.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

self.closeConstraint.animator().constant = showConstant
self.handledViewController.view.layer?.transform = CATransform3DIdentity
self.toggleButton.animator().title = "Hide"
disclosed = false
}
NSAnimationContext.runAnimationGroup(grow, completionHandler: {})
}
}
J'ai peu joué avec les animations au delà  de l'utilisation de .animator() jusqu'à  présent, il est possible que j'oublie quelque chose.
 
Une idée ou c'est un bug ?

Réponses

  • CéroceCéroce Membre, Modérateur
    Je ne connais pas les détails du fonctionnement, mais j'évite de mélanger animator proxy et Core Animation.
    En effet, le premier s'applique à  la vue, et notamment à  sa frame, alors que Core Animation s'applique à  la layer, ce qui n'impacte pas la frame de la vue " alors que l'inverse, si.
    Ainsi, on peut avoir une view frame à  un endroit et la layer frame à  un autre.

    Personnellement, je modifierais les deux frames par des contraintes.
  • Je me suis bien douté qu'utiliser Core Animation et les animator proxies pouvait être source d'ennui... 


    Par contre je ne comprends pas pourquoi ça fonctionne pour le "grow" mais pas pour le "shrink" il doit y avoir un truc...


     


    Sinon que veux-tu dire par "les deux frames" Céroce ? Tu suggèrerai de faire un setFrame: sur la subview ? J'aurai pas le même effet d'écrasement si ?


  • CéroceCéroce Membre, Modérateur


    Sinon que veux-tu dire par "les deux frames" Céroce ?




     


    Je me suis sans doute trompé, parce que je ne sais pas à  quelle vue s'applique la contrainte, et pas bien ce que tu cherches à  faire.


    Veux-tu cet effet d'écrasement ou pas, déjà  ?

  • Alors j'ai réfléchis à  la chose et passé pas mal de temps à  travailler dessus ce matin. 


    Exit CoreAnimation et les layers j'ai utilisé la property bound de la view. Ce qui nous donne ce code :



    @IBAction func toggle(sender: AnyObject) {
    var size = self.handledViewController.view.bounds.size
    var position = self.handledViewController.view.bounds.origin

    if !disclosed {
    func shrink(ctx: NSAnimationContext!) {
    var factor: CGFloat = 8.0

    size.height *= factor
    position.y = -size.height + NSHeight(self.handledViewController.view.frame)

    ctx.duration = self.animationDuration
    ctx.allowsImplicitAnimation = true
    ctx.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)

    self.closeConstraint.animator().constant = 0.0
    self.handledViewController.view.animator().bounds = NSRect(origin: position, size: size)
    self.handledViewController.view.needsDisplay = true
    self.toggleButton.animator().title = "Show"

    disclosed = true
    }
    NSAnimationContext.runAnimationGroup(shrink, completionHandler: {})
    } else {
    func grow(ctx: NSAnimationContext!) {
    size.height = NSHeight(handledViewController.view.frame)
    position.y = 0.0

    ctx.duration = self.animationDuration
    ctx.allowsImplicitAnimation = true
    ctx.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)

    self.closeConstraint.animator().constant = showConstant
    self.handledViewController.view.animator().bounds = NSRect(origin: position, size: size)
    self.handledViewController.view.needsDisplay = true
    self.toggleButton.animator().title = "Hide"

    disclosed = false
    }
    NSAnimationContext.runAnimationGroup(grow, completionHandler: {})
    }
    }

    Avec le résultat suivant (pour un animationDuration assez épais) :


     



     


    On voit bien qu'il y a un sacré décalage entre l'animation de la fenêtre et le contenu de la view...


    Là  par contre j'ai du mal à  bien calculer mon coup si quelqu'un a déjà  eu un soucis de ce genre à  régler je suis preneur. C'est sûrement qu'une histoire de maths mais c'est pas vraiment mon domaine de prédilection 😧 


Connectez-vous ou Inscrivez-vous pour répondre.