Quand layoutSubviews appelé?
-
05-09-2019 - |
Question
J'ai une vue personnalisée qui n'est pas de recevoir des messages de layoutSubview
pendant l'animation.
J'ai une vue qui remplit l'écran. Il a une sous-vue personnalisé au bas de l'écran qui redimensionne correctement dans Interface Builder si je change la hauteur de la barre de navigation. layoutSubviews
est appelée lorsque la vue est créée, mais jamais à nouveau. Mes subviews sont posés correctement. Si je bascule l'appel en barre d'état au large, n'est pas appelé le layoutSubviews
du sous-vue du tout, même si la vue principale ne d'animer Redimensionner.
Dans quelles circonstances est layoutSubviews
effectivement appelé?
Je autoresizesSubviews
mis à NO
pour mon affichage personnalisé. Et dans Interface Builder je le haut et en bas et jambes de force l'ensemble flèche verticale.
Une autre partie du casse-tête est que la fenêtre doit être clé:
[window makeKeyAndVisible];
d'autre les sous-vues ne sont pas automatiquement redimensionnées.
La solution 5
Je pisté la solution jusqu'à l'insistance d'Interface Builder que les sources ne peuvent pas être modifiés sur une vue qui a les éléments d'écran simulé sous tension (barre d'état, etc.). Étant donné que les sources étaient hors de la vue principale, ce point de vue ne pouvait pas changer la taille et donc défilait dans son intégralité lorsque la barre en appel est apparu.
Pour activer les fonctions simulées hors tension, puis redimensionner la vue et le réglage des ressorts correctement causé l'animation de se produire et ma méthode à appeler.
Un problème supplémentaire dans le débogage est que le simulateur quitte l'application lorsque l'état en appel est activée via le menu. Quitter app = pas débogueur.
Autres conseils
J'avais une question similaire, mais n'a pas été satisfait de la réponse (ou tout ce que je pouvais trouver sur le net), donc je l'ai essayé dans la pratique et est ici ce que je suis:
-
init
ne cause pas àlayoutSubviews
être appelé (duh) - causes
addSubview:
layoutSubviews
à appeler le vue ajoutée, la vue, il est d'être ajouté à (voir la cible), et tout le subviews de la cible
vue -
setFrame
appelle intelligemment surlayoutSubviews
la vue ayant son cadre défini que si le paramètre de taille de la trame est différent - un défilement UIScrollView
les causes
layoutSubviews
à être appelés le ScrollView, et son superview - rotation un dispositif uniquement les appels
layoutSubview
sur la vue parent ( viewControllers primaire répondant vue) - Redimensionnement une vue appellera
layoutSubviews
sur son superview
Mes résultats - http: // blog. logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/
Miser sur la réponse précédente @BadPirate, j'ai expérimenté un peu plus loin et est venu avec quelques éclaircissements / corrections. Je trouve que layoutSubviews:
sera appelé une vue si et seulement si:
- Ses propres limites (cadre non) ont changé.
- Les limites de l'un de ses sous-vues directes ont changé.
- A sous-vue est ajouté à la vue ou retiré de la vue.
Quelques détails pertinents:
- Les limites sont considérées comme modifiées que si la nouvelle valeur est différente, y compris une autre origine . Notez précisément c'est pourquoi
layoutSubviews:
est appelée chaque fois qu'un défile UIScrollView, car il effectue le défilement en changeant l'origine de ses limites. - Modification du cadre ne change les limites si la taille a changé, car cela est la seule chose propagée à la propriété bounds.
- Un changement dans les limites d'une vue qui ne sont pas encore dans une hiérarchie de vue se traduira par un appel à
layoutSubviews:
lorsque la vue est finalement ajouté à une hiérarchie de vue . - Et pour être complet: ces déclencheurs ne le font pas directement appeler layoutSubviews, mais plutôt appeler
setNeedsLayout
, qui fixe / lève un drapeau. Chaque itération de la boucle d'exécution, pour toutes les vues dans la hiérarchie de la vue , ce drapeau est cochée. Pour chaque vue où le drapeau est trouvé levé,layoutSubviews:
est appelé et le drapeau est remis à zéro. Vues plus haut dans la hiérarchie seront vérifiés / appelé en premier.
changements de mise en page peuvent se produire lorsque l'un des événements suivants se produit en vue:
a. La taille des changements de rectangle de limites d'une vue.
b. Un changement d'orientation de l'interface se produit, ce qui déclenche généralement un changement dans le rectangle des limites de la vue racine.
c. L'ensemble des sous-couches d'animation de base associés aux changements de la couche de la vue et nécessite la mise en page.
ré. Vos forces d'application mise en page se produire en appelant la méthodesetNeedsLayout
oulayoutIfNeeded
d'une vue.
e. Vos forces d'application mise en page en appelant la méthodesetNeedsLayout
d'objet de couche sous-jacente de la vue.
Certains des points de réponse de BadPirate ne sont que partiellement vrai:
-
Pour le point de
addSubView
addSubview
fait layoutSubviews à appeler à la vue étant ajoutée, la vue, il est ajouté à (vue cible), et tous les sous-vues de la cible.Cela dépend (vue cible) masque de redimensionnement automatique de la vue. Si elle a autoresize masque, layoutSubview sera appelé à chaque
addSubview
. Si elle n'a pas de masque autoresize alors layoutSubview sera appelée que lorsque la taille cadre (cible) Voir de la vue change.Exemple: si vous avez créé UIView programme (il n'a pas de masque autoresize par défaut), LayoutSubview sera appelée que lorsque le cadre UIView ne change pas sur tous les
.addSubview
Il est par cette technique que la performance de l'application augmente également.
-
Pour le point de rotation de l'appareil
La rotation d'un dispositif appelle uniquement layoutSubview sur la vue parent (la vue principale de répondre viewController)
Cela peut être vrai que lorsque votre CV est dans la hiérarchie VC (racine à
window.rootViewController
), ce bien est le cas le plus courant. Dans iOS 5, si vous créez un CV, mais il est pas ajouté dans une autre VC, alors ce VC n'obtiendrait pas remarqué quand rotate de l'appareil. Par conséquent, son point de vue ne serait pas se faire remarquer en appelant layoutSubviews.
appel
[self.view setNeedsLayout];
en viewController fait appeler viewDidLayoutSubviews
Avez-vous regardé layoutIfNeeded?
L'extrait de la documentation est ci-dessous. Est-ce que le travail d'animation si vous appelez cette méthode explicitement lors de l'animation?
layoutIfNeeded Expose les sous-vues si nécessaire.
- (void)layoutIfNeeded
Discussion Utilisez cette méthode pour forcer la mise en page des sous-vues avant de tirer.
Disponibilité Disponible dans l'iPhone OS 2.0 et versions ultérieures.
Lors de la migration d'une application OpenGL du SDK 3 à 4, layoutSubviews n'a pas été appelé plus. Après beaucoup d'essais et d'erreurs j'ai finalement ouvert MainWindow.xib, sélectionné l'objet de la fenêtre, dans l'inspecteur a choisi la fenêtre onglet Attributs (à gauche) et vérifié « Visible au lancement ». Il semble que dans SDK 3 il encore utilisé pour faire un appel layoutSubviews, mais pas 4.
6 heures de frustration mis fin.
Un cas assez obscure, mais potentiellement important quand layoutSubviews
ne fait jamais appelé est:
import UIKit
class View: UIView {
override class var layerClass: AnyClass { return Layer.self }
class Layer: CALayer {
override func layoutSublayers() {
// if we don't call super.layoutSublayers()...
print(type(of: self), #function)
}
}
override func layoutSubviews() {
// ... this method never gets called by the OS!
print(type(of: self), #function)
}
}
let view = View(frame: CGRect(x: 0, y: 0, width: 100, height: 100))