[Pour les débutants Swift] J'ai essayé de résumer le cycle de mise en page désordonné de ViewController et View

https___qiita-image-store.s3.amazonaws.com_0_45525_670d0038-6f03-095f-ca22-90c510f8babf.png Je suis kyoya, stagiaire dans une entreprise auto-développée. Étant donné que ma connaissance du cycle de mise en page de ViewController et UIView était gâchée en moi, j'ai décidé de l'organiser en moi-même et de la résumer avec le sens de l'explication pour les débutants.

Connaissances préalables

À propos de la relation entre ViewController et View

Étonnamment, beaucoup de gens pensent que ViewController et View sont confondus, alors organisons-les une fois ici.

Ce contrôleur de vue, qui est généré lorsque vous lancez un projet avec Xcode, est comme vous si vous êtes débutant. Ce n'est pas grave si vous pensez que ce contrôleur de vue gère l'affichage de la vue d'une manière très simple. Et une vue est associée à un contrôleur de vue. Lors de la création d'un écran, placez des boutons et des étiquettes sur cet UIView lié. (Lorsque deux vues se chevauchent, la vue de la hiérarchie inférieure est appelée vue parente (Super View) et la vue de la hiérarchie supérieure est appelée vue enfant (Vue secondaire))

À propos du cycle de vie

J'expliquerai le cycle de vie, qui fait également l'objet de cet article. Le cycle de vie est un ensemble de processus permettant d'afficher l'écran. Par exemple, divers processus tels que la lecture du contrôleur de vue en mémoire, le calcul des informations de position de la vue à afficher et l'affichage réel de la vue à l'écran. Est en cours d'exécution. Et comme je l'ai dit, il existe deux types de traitement dans le cycle de vie: le traitement du côté du contrôleur de vue et le traitement du côté de la vue. Il y a un moment pour afficher la vue dans le ViewController, mais il est normal de comprendre que le traitement du côté de la vue y est effectué.

Traitement du cycle de vie ViewController

Je vais vous expliquer le traitement du cycle de vie du contrôleur de vue. À partir de la conclusion, le processus se déroulera comme l'image ci-dessous. https___qiita-image-store.s3.amazonaws.com_0_45525_670d0038-6f03-095f-ca22-90c510f8babf.png

En disant que vous êtes arrivé à cet article, vous avez probablement vu des images comme celle-ci plusieurs fois. Et à chaque fois, certaines personnes peuvent penser qu'elles ne peuvent pas se souvenir ou comprendre. Ça va. Je vais l'expliquer d'une manière facile à comprendre.

Tout d'abord, le cycle de vie du contrôleur de vue peut être grossièrement divisé en trois étapes. (ViewWillDissappear et viewDidDisappear au moment de la transition de vue sont omis) Charger ViewController → Afficher la vue de ViewController → Une fois l'affichage terminé Regardons chacun d'eux

Première étape: chargement du contrôleur de vue

Tout d'abord, enregistrez le contrôleur de vue correspondant à l'écran à afficher dans la mémoire (comme une zone de données). Après l'enregistrement, même si vous effectuez une transition d'écran, elle ne disparaîtra pas de la mémoire, elle ne sera donc appelée qu'au premier affichage. Les deux méthodes suivantes sont appelées ici.

loadView()
viewDidLoad()
viewWillAppear()

loadView () '' est le processus d'enregistrement réel du contrôleur de vue en mémoire. viewDidLoad () '' est un processus qui est appelé après l'enregistrement en mémoire. LoadView () '' a fini de charger le contrôleur de vue, et le chargement de la vue maintenue est également loadView () ''. Puisqu'il se termine par , dans viewDidLoad () '', vous pouvez définir les propriétés de chaque vue (paramètre de couleur d'arrière-plan, paramètre de texte d'étiquette, etc.) comme indiqué ci-dessous.

    override func viewDidLoad() {
        super.viewDidLoad()
        testView.backgroundColor = .red
    }

viewWillAppear () '' est appelé avant d'afficher l'image. Ceci est également appelé lors du changement de TabBars, comme il est appelé à chaque fois que l'écran est affiché. Contrairement à viewDidLoad () '', il se caractérise par le fait qu'il est appelé plusieurs fois.

Affichage de la vue de la deuxième étape

Après avoir lu dans la mémoire et défini les propriétés de chaque vue, la vue s'affichera ensuite. Les deux méthodes suivantes sont appelées ici.

viewWillLayoutSubViews()
viewDidLayoutSubViews()

viewWillLayoutSubVIews () '' est appelé avant le début de la mise en page View. Il est également appelé lorsque l'écran pivote, lorsque la taille de la vue est modifiée ou lorsque la vue est supprimée ou ajoutée. Et après cela, nous entrerons dans la mise en page de View (décrite plus tard) viewDidLayoutSubViews () '' est appelé une fois la disposition de la vue terminée. Donc, ici, la disposition de la vue est terminée, et la position et la taille de la vue sont décidées.

Troisième étape: après l'affichage de la vue

La méthode suivante est appelée ici.

viewDidAppear()

viewDidAppear () '' est appelé une fois l'affichage de l'écran terminé. Donc, le processus que vous ajoutez ici est fondamentalement sans rapport avec l'expérience utilisateur. Par exemple, afficher les journaux. De plus, puisque cette méthode est associée à viewWillAppear () '', elle est également appelée à chaque fois que l'écran est affiché.

C'est l'explication du cycle de vie du contrôleur de vue lui-même.

Afficher le cycle de vie

À partir de là, nous passerons au cycle de vie de la vue. Ce processus est appelé après le précédent `` viewWillLayoutSubViews () ''. Le cycle de vie de la vue est affiché à travers le processus suivant. Mise à jour des contraintes → Mise à jour des informations du cadre → Rendu (processus de dessin) Regardons chacun d'eux

Première étape: mise à jour des contraintes

Le processus de mise à jour de la contrainte est exécuté lorsque la contrainte change. Le processus qui provoque le changement de contrainte est le suivant.

-Ajouter ou supprimer des contraintes ・ Changer la priorité des contraintes -Changer la hiérarchie de la vue contrainte

Ainsi, lorsque vous exécutez `` NSLayoutConstraints.activate ([~~~]) '', updateConstrains () sera appelé.

Lorsque le processus de mise à jour de la contrainte est exécuté, la fonction «updateConstraints ()» de la vue qui a la contrainte est appelée. Soyez prudent ici. Vous pourriez penser à «updateConstraints ()» comme mélangé avec la méthode de ViewController, mais comme le ViewController lui-même ne peut pas être contraint, cette méthode est la propre méthode de View. (Ce que View a signifie qu'il a également UIButton et UILabel qui héritent de la classe UIView).

Appeler la mise à jour de la contrainte à tout moment

Il est également possible d'appeler le processus de mise à jour des contraintes à tout moment. Vous pouvez appeler ʻupdateConstraints () ʻune vue qui nécessite des mises à jour de contraintes en appelant` ʻupdateConstraintsIfNeeded () . Vous pouvez également marquer la vue comme "mise à jour de contrainte requise" en appelant setNeedsUpdateCOnstrains () ''.

* Attention * La plage d'influence de updateCOnstrainsIfNeeded () est elle-même et sa sous-vue.

C'est compliqué, mais comme ʻupdateConstrainsIfNeeded () applique le traitement de mise à jour de contrainte à sa propre vue et à sa sous-vue, par exemple, même si vous exécutez ʻupdateConstrainsIfNeeded () sur une certaine vue, son parent Même si l'indicateur de mise à jour de la contrainte est défini dans View, aucun traitement n'est effectué.

C'est un peu compliqué, donc pour résumer, appeler ʻupdateConstrainsIfNeeded () appelle ʻupdateConstrains () de la vue qui a appelé `` setNeedsUpdateConstrains () '' dans sa propre vue et sa sous-vue. ..

De plus, vous pouvez également mettre à jour la contrainte que `` setNeedsUpdateConstrains () '' a été appelée au moment de la prochaine mise à jour de la contrainte (le timing est laissé au système, mais celui-ci est recommandé * la raison sera décrite plus tard)

Deuxième étape: mise à jour des informations sur la trame

J'espère que vous pouvez considérer les informations du cadre comme des informations telles que la position et la taille de la vue. Ce processus est appelé lorsque les informations de trame sont mises à jour. Les déclencheurs à appeler sont les suivants.

-Changer le cadre de vue -Lorsqu'une vue est ajoutée ou supprimée ・ (En prime) Lorsque le contentOffSet du ContentView du `ʻUIScrollView`` est modifié (simplement, lorsque les coordonnées des éléments dans le ScrollView sont modifiées, la position d'affichage est modifiée par l'utilisateur. Il y a des moments où vous souhaitez unifier et afficher au centre du parchemin au lieu de le laisser décider, et l'utiliser dans de tels cas.)

La méthode appelée ici est layoutSubViews () ''. Cette méthode utilise les informations de contrainte pour créer les informations de trame. Vous pouvez appeler layoutSubViews () '' à tout moment, tout comme la mise à jour des contraintes. L'appel de layoutIfNeeded () est marqué comme "les informations sur le cadre doivent être mises à jour" (peut être marqué en appelant setNeedsLayoutSubViews ()) des vues auto et enfant Vous pouvez exécuter layoutSubViews (). De plus, les vues marquées avec `` setNeedsLayoutSubViews () '' seront mises à jour immédiatement au moment de la prochaine mise à jour de l'image (la synchronisation est laissée au système, mais celle-ci est recommandée * la raison sera décrite plus tard) C'est similaire à la mise à jour de, donc je ne vais pas l'expliquer en détail.

LayoutSubViews () lu à tout moment est exécuté dans le thread principal

J'ai mentionné précédemment que layoutSubViews () qui a été exécuté au moment du système est meilleur, mais je vais expliquer pourquoi. Il en va de même pour updateConstraints () pour la mise à jour des contraintes. Veuillez noter que le traitement de layoutSubViews () '' appelé par layoutSubViewsIfNeeded () '' est exécuté dans le thread principal. Cela peut ne pas être familier aux débutants, mais comprenez que le fil principal est comme la zone où le traitement a lieu. Et dans ce fil principal, des modifications de l'interface utilisateur sont également apportées ici. Par conséquent, si vous utilisez le thread principal avec layoutSubViewsIfNeeded () '', la mise à jour de l'interface utilisateur s'arrêtera pendant ce temps, ce qui peut affecter l'expérience utilisateur. Dans le cas d'un traitement qui semble être lié à l'interface utilisateur, appelons directement layoutSubViews () ''. `` LayoutSubVIews () '' appelé à un rythme régulier peut être exécuté sans occuper le thread principal, donc cela n'affecte pas les modifications de l'interface utilisateur.

Précautions lors du dépassement

Il y a deux points à noter lors du remplacement. Le premier point est que vous devez d'abord exécuter super.layoutSubViews () ''. Le traitement de la mise à jour du cadre est effectué par super.layoutSubViews () ''. Si vous ne le faites pas en premier, vous devrez écrire votre propre traitement sans mettre à jour le cadre, qui peut être un foyer de bogues. Le deuxième point est que les informations sur le cadre sont mises à jour à partir de la vue parente de manière descendante. Étant donné que la mise à jour est descendante, je peux modifier la disposition de la vue enfant, mais si je modifie la vue parente, `` layoutSubViews () '' sera appelé à nouveau et il plantera.

Rendu de la troisième étape (vue du dessin)

Ici, la vue est en fait dessinée à l'écran en utilisant les informations de cadre mises à jour. Ce processus est appelé au moment suivant.

-Déplacez ou supprimez la vue cachée ・ Lorsque la vue masquée est affichée -Faites défiler la vue vers l'extérieur de l'écran et revenez à l'écran.

La méthode appelée ici est drawRext (_ :) ''. Si vous souhaitez l'appeler à tout moment, vous pouvez l'appeler explicitement en le marquant avec setNeededDisplay () '' ou `` setNeedsDisplayInRect () ''.

C'est tout pour cet article. Il est devenu plus volumineux que je ne l'imaginais, mais veuillez vous y référer. J'écrirai un article s'il y a de nouvelles découvertes.

Merci beaucoup.

Recommended Posts

[Pour les débutants Swift] J'ai essayé de résumer le cycle de mise en page désordonné de ViewController et View
J'ai essayé de résumer les bases de kotlin et java
J'ai essayé de résumer les méthodes de Java String et StringBuilder
J'ai essayé de résumer les points clés de la conception et du développement de gRPC
J'ai brièvement résumé la grammaire de base de Ruby
J'ai essayé de résumer les méthodes utilisées
J'ai essayé de résumer l'API Stream
[Rails] Articles pour les débutants pour organiser et comprendre le flux de form_with
J'ai essayé de mesurer et de comparer la vitesse de Graal VM avec JMH
[Pour les débutants] DI ~ Les bases de DI et DI au printemps ~
05. J'ai essayé de supprimer la source de Spring Boot
J'ai essayé de réduire la capacité de Spring Boot
[Swift] J'ai déjà beaucoup d'informations, mais j'ai essayé de résumer le casting (comme, comme!, Comme?) À ma manière.
J'ai essayé de vérifier ceci et celui de Spring @ Transactional
J'ai essayé JAX-RS et pris note de la procédure
[Rails 6.0, Docker] J'ai essayé de résumer la construction de l'environnement Docker et les commandes nécessaires pour créer un portfolio
J'ai essayé de résumer les applications et les outils de développement personnellement utiles (outils de développement)
J'ai essayé de créer un environnement de WSL2 + Docker + VSCode
J'ai essayé de résumer les applications et les outils de développement personnellement utiles (Apps)
J'ai essayé d'expliquer ce que vous pouvez faire dans un langage populaire pour le développement Web du point de vue d'un débutant.
J'ai essayé de me permettre de définir le délai pour le client Android UDP
Je vais expliquer l'imbrication des déclarations qui tuent les débutants
J'ai essayé de résoudre le problème de la "sélection multi-étapes" avec Ruby
[Swift5] Comment obtenir un tableau et un ensemble de différences entre les tableaux
J'ai essayé de créer un environnement de serveur UML Plant avec Docker
L'histoire de Collectors.groupingBy que je veux garder pour la postérité
[Rubiy] J'ai essayé de résumer le traitement de la boucle ce soir [fois, pause ...]
Conférence spéciale sur la simulation multi-échelles: j'ai essayé de résumer le 5e
[Astuces] Comment résoudre les problèmes avec XCode et Swift pour les débutants
Conférence spéciale sur la simulation multi-échelles: j'ai essayé de résumer le 8
J'ai essayé de vérifier le fonctionnement du serveur gRPC avec grpcurl
Conférence spéciale sur la simulation multi-échelles: j'ai essayé de résumer le 7
J'ai essayé de résoudre le problème de Google Tech Dev Guide
J'ai essayé de résumer le support d'iOS 14
J'ai essayé de résumer l'apprentissage Java (1)
J'ai essayé de résumer Java 8 maintenant
[Ruby] Je souhaite extraire uniquement la valeur du hachage et uniquement la clé
[Introduction à Java] J'ai essayé de résumer les connaissances que j'estime essentielles
Avant d'oublier, les fonctions et les points de l'application Furima
Je veux passer l'argument d'Annotation et l'argument de la méthode d'appel à aspect
J'ai essayé d'utiliser pleinement le cœur du processeur avec Ruby
J'ai essayé de visualiser l'accès de Lambda → Athena avec AWS X-Ray
[Ruby] J'ai essayé de résumer les méthodes fréquentes dans paiza
[Ruby] J'ai essayé de résumer les méthodes fréquentes avec paiza ②
J'ai essayé de traduire la grammaire de R et Java [Mis à jour de temps en temps]
Je veux que vous utilisiez Enum # name () pour la clé de SharedPreference
(Pour les débutants) Collection d'éléments de vue Swift UI
J'ai essayé de résumer les expressions Java lambda
J'ai essayé d'implémenter le modèle Iterator