Redimensionner les éléments enfants lorsque le frame de la UIView parente est modifiée avec une a
Bonjour à tous !
Je rencontre actuellement une petite difficulté. Je souhaiterais que les frames des views contenues dans une View soient modifiés progressivement lorsque cette dernière connait un changement de proportion.
J'ai testé en implémentant la méthode layoutSubviews mais je n'accède qu'à la valeur du frame finale et non aux valeurs intermédiaires. J'ai également ajouté une observation sur le changement du frame mais je n'ai rien de mieux.
Comment puis-je procéder pour obtenir une animation cohérente ?
Pour information, je crée ma vue via du code Swift. Je n'utilise pas xib.
Voici un code d'exemple qui illustre ma demande. Dans le cas ci-dessous, je souhaiterais que la largeur de titleView soit modifiée en temps réel lorsque la largeur de MyView est modifiée via une animation.
public class MyView: UIView {
private lazy var titleView: UIView = {
let x = CGFloat(0.0)
let y = CGFloat(0.0)
let width = self.frame.width
let height = CGFloat(20.0)
return UIView(frame: CGRect(x: x, y: y, width: width, height: height)
}()
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
private func setup() {
self.addSubview(titleView)
}
public override func layoutSubviews() {
...
}
}
public class View: UIView {
private lazy var myView: MyView = {
let x = CGFloat(0.0)
let y = CGFloat(0.0)
let width = CGFloat(600.0)
let height = CGFloat(300.0)
return UIView(frame: CGRect(x: x, y: y, width: width, height: height)
}()
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
private func setup() {
self.addSubview(myView)
}
public func animate() {
UIView.animate(withDuration: 0.5, delay: 0.0, option: .curveEaseInOut, animation: {
self.myView.frame = CGRect(x: CGFloat(0.0), y: CGFloat(0.0), width: CGFloat(300.0), height: CGFloat(300.0))
})
}
}
Je viens d'écrire le code ci-dessous sur le forum mais je ne sais pas si il compile... ::) L'idée est que vous ayez un aperçu de ma demande.
Merci pour vos réponses !
Réponses
Hérétique !
Cela devrais fonctionner normalement si la vue était définie par rapport à son contenant avec des contraintes Storyboard.
T'as essayé de redéfinir ta vue avec drawRect ?
:P :P :P
Je ne dis pas que "ça ne fonctionne pas". Ma vue enfant est bien redimensionnée mais pas de façon progressive en fonction de l'animation. Tu vois le truc ?
Non, tu peux m'en dire plus ?
Oui, je vois très bien. Et je maintient que la vue enfant devrais (normalement) être redimensionnée progressivement pendant l'animation, si elle était définie par des contraintes par rapport à son parent.
Je tape un petit code et je te donne ça .. demain ! Il se fait tard, là .
Via des NSLayoutConstraint ?
Bon, je sèche. je pensais y arriver facilement et .. euh .. Cela ne fonctionne pas avec drawRect, ni avec les contraintes Storyboard. Je dois me tromper dans un paramètre d'animation. Affaire à suivre ..
EDIT : J'ai trouvé. Cela m'agaçait tellement que je n'arrivais pas à dormir.
Oui
Code et explication demain. Les NSLayoutConstraint sont construites avec Storyboard mais on peut le faire en code aussi (à condition d'aimer souffrir !).
Bon, je ne vais pas t'infliger les NSLayoutConstraints. J'ai trouvé comment faire avec les vieilles méthodes de grand-maman.
Ton problème venait du réglage de l'animation. Il faut ajouter .layoutSubviews dans les options pour forcer iOS à recalculer les subviews pendant l'animation. J'ai aussi ajouté .allowAnimatedContent par prudence, même si ce n'est pas vraiment nécessaire dans ce cas précis.
Pour tester, j'ai tapé un petit exemple avec une sous-vue dont la taille est égale à son parent, moins une marge de 5 points
Merci Draken !
Et du coup ta solution fonctionne sans utiliser de NSLayoutConstraint ?
Tu vois un NSLayoutConstraint dans le programme d'exemple ?
Arf... Désolé, je n'avais pas pris le temps de bien lire ton post précédent...
En te basant sur ton exemple, tu aurais la possibilité de faire une version en utilisant les NSLayoutConstraint ? C'est pour voir la différence entre les deux solutions...
Merci en tout cas pour ton retour.
Tu veux dire la solution que j'ai testé cette nuit ?
Première étape :
J'ai créé deux vue sous Storyboard, avec des contraintes liant le parent (MonParent) à son enfant (enfant).
J'ai ensuite tracé un IBOulet pour accéder à MonParent dans le corps du code.
Sa taille est définie par deux contraintes :
HauteurParent : MonParent.Height = 100 ([objet MonParent] [Relation Equal] [Constant = 100]
LargeurParent : MonParent.Width = 100
Normalement les contraintes sont fixes, mais on peut définir des IBOulets pour les manipuler avec le code. C'est ce que j'ai fait en créant les IBOutlets HauteurParent et LargeurParent.
Seconde étape :
Pour réaliser l'animation dans le code, j'ai défini les nouvelles valeurs avant l'animation et animer la mise à jour du layout.
J'aurais pu définir les contraintes en code, mais c'est une galère, alors que cela ne prend que quelques instants avec Storyboard.
Du coup, le code ci-dessus resterait inchangé mise à par le fait de créer les contraintes à la mano ?
Si je spécifie via du code que l'élément enfant à des contraintes de 5 pts par rapport aux bords de la vue parente (imagine un rectangle dans ton rectangle comme dans ton exemple). Si je modifie le frame de la vue parente via l'animation suivante :
La vue fille se verra également redimensionnée en respectant les contraintes ?
Je n'ai pas essayé, mais normalement oui. Je n'ai créé des contraintes par code, qu'une seule fois, dans le cadre d'un exercice du MOOC du professeur Kordon. C'était long, compliqué et galère. Définir une marge de 5 pixels avec Storyboard se fait en moins de 20 secondes.
Si la programmation par code des contraintes t'intéresse tu devrais allez voir les vidéos (en français) de F. Kordon sur le sujet, partie 4 de la section 6 de ces cours. Le lien est en rouge dans ma signature.
C'est ce que j'ai essayé en premier, mais cela ne fonctionne pas. C'est pourquoi j'ai réalisé l'animation en ajoutant des contraintes de taille pour la vue parente.
ça marche !
Pour récapituler :
1 - Créer le la vue parente en utilisant des contraintes pour les valeurs width et height
2 - Créer la vue enfant let v = UIView(frame : CGRect(x: x, y: y, width: width, height: height))
3 - Ajouter mes contraintes pour que les proportions de ma vue enfant soit définie en fonction de ma vue parente
Lors du point 2, si je sette width et height à CGFloat(0.0), les contraintes doivent correctement ajuster la vue fille, non ?
Au point 2, tu doit créer la vue enfant sans lui donner de taille précise. Ce sont les contraintes qui vont calculer automatiquement la frame.
Oui, si tu agit sur les contraintes définissant la taille de la vue parente, les contraintes de la vue fille la forcent à s'adapter. C'est ce que j'ai fait dans mon code :
Dans le cas ci-dessus, je me demande si en ajoutant dans les options un paramètre (si il existe) qui force un recalcule en fonction des contraintes, je pourrais retomber sur mes pattes...
Elle est bien cachée, si elle existe :
https://developer.apple.com/documentation/uikit/uiviewanimationoptions
D'accord ! Merci bien !
Je suis en train de faire des tests par rapport à ce que tu m'as indiqué histoire d'apprendre à utiliser les contraintes.
Dans la méthode updateFrame, je souhaite mettre à jour la hauteur de ma vue. Seulement, une fois la nouvelle valeur settée, la mise à jour n'apparait pas à l'écran. Quelle fonction faut il appliquer pour que d'un point de vue rendu graphique, la mise à jour soit effective ?
Euh .. je ne sais plus. C'est trop compliqué pour moi les AutoLayout par code, alors que c'est si simple avec Storyboard. Tu devrais vraiment regarder les vidéos du prof Kordon sur l'utilisation des Autolayout par code. Il donne des exemples. J'en avais vraiment galèré à l'époque de son MOOC (Mars 2017).
Je viens de trouver. Je n'avais pas setté à false la valeur de la propriété translatesAutoresizingMaskIntoConstraints. Maintenant ça fonctionne parfaitement.
Merci Draken pour ton aide ! Je pousserai les tests dans les jours à venir.
C'est clair qu'en n'activant pas l'AutoLayout cela ne risquait pas de fonctionner. J'avais été surpris à l'époque de voir qu'il fallait mettre la valeur false pour l'activer. Storyboard gère automatiquement tous ces fichus paramètres.
Merci bien Draken !
J'arrive à créer le comportement que je voulais. Pour faire simple, j'ai repris ta solution mais en codant les contraintes. Si tu maà®trises le procédé sur xib, tu arrives à t'en sortir. Par contre ce n'est pas du tout intuitif, tu n'as pas d'infobulle pour t'avertir qu'il y a un conflit entre tes NSLayoutConstraint...
Par contre (chose qui est peut être étrange), l'animation fonctionne bien (les éléments suivent bien également) avec le code ci-dessous:
Good !
Bon travail.
Il doit y avoir une différence subtile entre l'utilisation de Storyboard et des Autolayout par code. Ou je me suis trompé dans mes tests, ce qui n'aurais rien d'étonnant. Mais la question m'intéresse, je m'y replongerais quand j'aurais un peu de temps libre.
J'avais zappé...
La même pour moi ! xd
Par défaut, le storyboard positionne la variable à false alors que via du code c'est la valeur true qui sera positionnée...
Les animations, je les appliquais directement sur les frames. Mais je vais jouer avec les NSLayoutContraints, ça me semble pus simple.