web-dev-qa-db-fra.com

Animer les modifications intrinsicContentSize

J'ai une sous-classe UIView qui dessine un cercle dont le rayon change (avec de belles animations rebondissantes). La vue décide de la taille du cercle.

Je veux que cette sous-classe UIView change sa taille de cadre pour correspondre aux changements animés du rayon du cercle, et je veux que ces changements modifient toutes les NSLayoutConstraints connectées à la vue (de sorte que les vues qui sont contraintes au bord du cercle se déplacent comme le redimensionne le cercle).

Je comprends que l'implémentation de -(CGSize)intrinsicContentSize et l'appel de invalidateIntrinsicContentSize lorsque les changements de rayon indiqueront les contraintes à mettre à jour, mais je n'arrive pas à comprendre comment animer les modifications dans intrinsicContentSize.

L'appel de invalidateIntrinsicContentSize à partir d'un bloc [UIView animateWith ... met à jour instantanément la disposition.

Est-ce même possible, et existe-t-il une solution de contournement/meilleure?

50
Loz

invalidateIntrinsicContentSize fonctionne bien avec les animations et layoutIfNeeded. La seule chose que vous devez considérer est que le changement de la taille intrinsèque du contenu invalide la disposition de la vue d'ensemble. Cela devrait donc fonctionner:

[UIView animateWithDuration:0.2 animations:^{
    [self invalidateIntrinsicContentSize];
    [self.superview setNeedsLayout];
    [self.superview layoutIfNeeded];
}];
57
stigi

La contrainte de largeur/hauteur n'aide pas? Gardez la référence de cette contrainte et ...

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:nil
                             attribute:NSLayoutAttributeNotAnAttribute
                            multiplier:1
                              constant:myViewInitialWidth];

... lorsque vous souhaitez animer le redimensionnement de myView, procédez comme suit ...

self.viewWidthConstraint.constant = 100; // new width
[UIView animateWithDuration:0.3 animations:^{ [view layoutIfNeeded]; }];

... faites la même chose pour la hauteur.

Cela dépend de vos autres contraintes, peut-être serez-vous obligé d'augmenter la priorité de ces deux contraintes.

Ou vous pouvez sous-classer UIView, ajouter - (void)invalidateIntrinsicContentSize:(BOOL)animated et le simuler par vous-même. Obtenez une nouvelle taille à partir de - (CGSize)intrinsicContentSize et animez-la en animant des contraintes de largeur/hauteur. Ou ajoutez une propriété pour activer/désactiver les animations et remplacer invalidateIntrinsicContentSize et le faire à l'intérieur de cette méthode. Plusieurs façons ...

3
zrzka

Swift version de la réponse de @ stigi qui a fonctionné pour moi:

UIView.animate(withDuration: 0.2, animations: {
    self.invalidateIntrinsicContentSize()
    self.superview?.setNeedsLayout()
    self.superview?.layoutIfNeeded()
})
2
nathangitter

Dans le code ci-dessous, la classe intrinsèque est la classe qui vient de changer sa taille lors du changement d'une variable. Pour animer la classe intrinsèque, utilisez uniquement le code ci-dessous. S'il affecte d'autres objets plus haut dans la hiérarchie des vues, remplacez la classe self.intrinsic par la vue de niveau supérieur pour setNeedsLayout et layoutIfNeeded.

[UIView animateWithDuration:.2 animations:^{
        self.intrinsicClass.numberOfWeeks=8;
        [self.intrinsicClass setNeedsLayout];
        [self.intrinsicClass layoutIfNeeded];
    }];
2
Ajaxharg

Rien de tout cela n'a fonctionné pour moi. J'ai un UILabel que je place avec un NSAttributedString. Le texte est multiligne et enveloppe les limites de Word. La hauteur est donc variable. J'ai essayé ça:

[UIView animateWithDuration:1.0f animations:^{
    self.label = newLabelText;
    [self.label invalidateIntrinsicContentSize];
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

Et un certain nombre de variations. Aucun travail. L'étiquette change immédiatement sa taille, puis glisse dans sa nouvelle position. L'animation de la position des étiquettes sur l'écran fonctionne donc. Mais l'animation du changement de taille de l'étiquette ne l'est pas.

1
drekka

Eh bien pour Swift 4/3 cela fonctionne et je pense que c'est la meilleure pratique. Si vous avez un UIView avec un UILabel dedans et que le UIView adapte le cadre du UILabel, utilisez ceci:

self.theUILabel.text = "text update"
UIView.animate(withDuration: 5.0, animations: {
   self.theUIView.layoutIfNeeded()
})

Le self.view.layoutIfNeeded () normal fonctionnera également la plupart du temps.

0
J. Doe