[For Swift beginners] I tried to summarize the messy layout cycle of ViewController and View

https___qiita-image-store.s3.amazonaws.com_0_45525_670d0038-6f03-095f-ca22-90c510f8babf.png I'm kyoya, an intern at a self-developed venture company. Since my knowledge of the layout cycle of ViewController and UIView was messed up in me, I decided to organize it in myself and summarize it with the meaning of the explanation for beginners.

Prerequisite knowledge

About the relationship between ViewController and View

Surprisingly, many people think that ViewController and View are confused, so let's organize them once here.

This view controller, which is generated when you launch a project with Xcode, is like you, if you are a beginner. It's okay if you think that this view controller manages the display of the view in a very simple way. And one view is associated with one view controller. When creating a screen, place buttons and labels on this linked UIView. (When two views overlap, the view in the lower hierarchy is called the parent view (Super View), and the view in the upper hierarchy is called the child view (Sub View))

About life cycle

I will explain the life cycle, which is the subject of this article. The life cycle is a collection of processes for displaying the screen. For example, various processes such as reading the view controller into memory, calculating the position information of the view to be displayed, and actually displaying the view on the screen. Is running. And as I said, there are two types of processing in the life cycle: the processing on the view controller side and the processing on the view side. There is a timing to display the View in the ViewController, but it is okay to understand that the processing on the View side is done there.

ViewController life cycle processing

I will explain the process of the life cycle of ViewController. From the conclusion, the processing proceeds like the image below. https___qiita-image-store.s3.amazonaws.com_0_45525_670d0038-6f03-095f-ca22-90c510f8babf.png

By saying that you arrived at this article, you probably have seen such images many times. And every time, some people may think that they cannot remember or understand. It's okay. I will explain it in an easy-to-understand manner.

First of all, the life cycle of the view controller can be roughly divided into three stages as follows. (ViewWillDissappear and viewDidDisappear at the time of View transition are omitted) Load ViewController → Display View that ViewController has → After display is finished Let's look at each one

First stage: Loading the view controller

First, register the view controller corresponding to the screen to be displayed in the memory (like a data area). After registration, it will not disappear from the memory even if the screen transition is performed, so it will be called only at the first display. The following two methods are called here.

loadView()
viewDidLoad()
viewWillAppear()

loadView () is the process that actually registers the view controller in memory. viewDidLoad () is a process called after registration in memory. LoadView () has finished loading the view controller, and loading of the held view is also loadView () ` Since it ends with, in viewDidLoad (), you can set properties for each View (background color setting, label text setting, etc.) as shown below.

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

viewWillAppear () is called before displaying the image. This is called every time the screen is displayed, so it is also called when switching TabBars. Unlike viewDidLoad (), it is characterized by being called many times.

Second stage View display

After reading to memory and setting properties for each View, the View will be displayed next. The following two methods are called here.

viewWillLayoutSubViews()
viewDidLayoutSubViews()

viewWillLayoutSubVIews () is called before the start of the View layout. It is also called when the screen is rotated, when the size of the view is changed, or when the view is deleted or added. And after this, we will enter the layout of View (described later) viewDidLayoutSubViews () is called after the View layout is finished. So here the layout of the View is finished, and the position and size of the View are decided.

Third stage: After the view is displayed

The following methods are called here.

viewDidAppear()

viewDidAppear () is called after the screen display is finished. So the process you add here is basically irrelevant to the user experience. For example, displaying logs. Also, since this method is paired with viewWillAppear (), it is also called every time the screen display is finished.

This is the explanation of the life cycle of the view controller itself.

View lifecycle

From here, we will move on to the View life cycle. This process is called after the previous viewWillLayoutSubViews (). The View life cycle is displayed through the following process. Constraint update → Frame information update → Rendering (drawing process) Let's look at each one

First stage: Constraint update

The constraint update process is executed when the constraint is changed. The process that causes the constraint change is as follows.

-Add or remove constraints ・ Change the priority of constraints -Change the hierarchy of the constrained View

So when you run NSLayoutConstraints.activate ([~~~]), updateConstrains () will be called.

When the constraint update process is executed, the View's ʻupdateConstraints () `` that has the constraint is called. Please be careful here. You might think of ʻupdateConstraints ()` as mixed with the ViewController's method, but since the ViewController itself can't be constrained, this method is the View's method. (What View has means that it also has UIButton and UILabel that inherit from the UIView class).

Call constraint update at any time

It is also possible to call the constraint update process at any time. You can call ʻupdateConstraints () of a View that requires constraint updates by calling ʻupdateConstraintsIfNeeded () . You can also flag the view as "constraint update required" by calling setNeedsUpdateCOnstrains ().

* Caution * The range of influence of updateCOnstrainsIfNeeded () is itself and its SubView.

It's confusing, but ʻupdateConstrainsIfNeeded () `` applies constraint update processing to its own View and its SubView, so even if you execute ʻupdateConstrainsIfNeeded ()` on a certain View, for example, the parent's Even if the constraint update flag is set in View, no processing is performed.

It's a bit complicated, so to summarize, calling ʻupdateConstrainsIfNeeded () calls ʻupdateConstrains () of the View that called setNeedsUpdateConstrains () in its own View and its SubView. ..

In addition, you can also update the constraint that setNeedsUpdateConstrains () was called at the timing of the next constraint update (timing is left to the system, but this one is recommended * the reason will be described later)

Second stage: Update of frame information

I hope you can think of frame information as information such as the position and size of the View. This process is called when the frame information is updated. The trigger to call is as follows.

-Change the frame of View -When a View is added or deleted -(As a bonus) when the contentOffSet of the ContentView of the `ʻUIScrollView`` is changed (simply, when the coordinates of the elements in the ScrollView are changed, the display position is the user. There are times when you want to unify and display in the center of the Scroll instead of letting it be decided, so use it in such cases.)

The method called here is layoutSubViews (). This method uses the constraint information to build the frame information. You can call layoutSubViews () at any time, just like updating constraints. Calling layoutIfNeeded () is flagged as "frame information needs to be updated" (can be flagged by calling setNeedsLayoutSubViews ()) of self and child views You can run layoutSubViews (). Also, Views flagged with setNeedsLayoutSubViews () will be updated at once at the timing of the next frame update (timing is left to the system, but this one is recommended * the reason will be described later) It's similar to the update of, so I won't explain it in detail.

LayoutSubViews () read at any time is executed in the main thread

I mentioned earlier that layoutSubViews (), which was executed at system timing, is better, but I will explain why. The same is true for updateConstraints () for updating constraints. Please note that the processing of layoutSubViews () called by layoutSubViewsIfNeeded () is executed in the main thread. It may not be familiar to beginners, but understand that the main thread is like the area where processing takes place. And in this main thread, UI changes are also made here. Therefore, if you use the main thread with layoutSubViewsIfNeeded (), the UI update will stop during that time, which may affect the user experience. In case of processing that seems to be related to UI, let's call layoutSubViews () directly by overriding. LayoutSubVIews (), which is called at regular timing, can be executed without occupying the main thread, so it does not affect UI changes.

Precautions when overriding

There are two points to note when overriding. The first point is that you need to execute super.layoutSubViews () first. The frame update process is performed by super.layoutSubViews (). If you do not do this first, you will have to write your own processing without updating the frame, which can be a hotbed of bugs. The second point is that the frame information is updated from the parent view top-down. Since the update is top-down, I can change the layout of the child view, but if I make a change to the parent view, layoutSubViews () will be called again and it will crash.

Third stage rendering (View drawing)

Here, the View is actually drawn on the screen using the updated frame information. This process is called at the following timing.

-Move or delete the hidden View -When the View that was hidden (hidden) is displayed -Scroll View to the outside of the screen and return to the screen.

The method called here is drawRext (_ :). If you want to call this at any time, you can call it explicitly by flagging it with setNeededDisplay () or setNeedsDisplayInRect ().

That's all for this article. It has become more voluminous than I imagined, but please refer to it. I will write an article if there are new discoveries.

Thank you very much.

Recommended Posts

[For Swift beginners] I tried to summarize the messy layout cycle of ViewController and View
I tried to summarize the basics of kotlin and java
I tried to summarize the methods of Java String and StringBuilder
I tried to summarize the key points of gRPC design and development
[Rails] I tried to summarize the passion and functions of the beginners who created the share house search site!
I tried to summarize the state transition of docker
[Beginner's point of view] I tried to solve the FizzBuzz problem "easily" with Ruby!
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
[Swift] I tried to implement the function of the vending machine
I tried to summarize the basic grammar of Ruby briefly
I tried to summarize the methods used
I tried to summarize the Stream API
[Rails] Articles for beginners to organize and understand the flow of form_with
I tried to measure and compare the speed of GraalVM with JMH
I don't really understand the difference between swift Error and NSError, so I tried to summarize it myself.
[For beginners] DI ~ The basics of DI and DI in Spring ~
05. I tried to stub the source of Spring Boot
I tried to reduce the capacity of Spring Boot
[Must-see for beginners] I changed the display of the posting time to Japanese time [l method]
[Swift] I already have a lot of information, but I tried to summarize the cast (as, as !, as?) In my own way.
[Swift] I tried to implement exception handling for vending machines
I tried to verify this and that of Spring @ Transactional
I tried JAX-RS and made a note of the procedure
[Rails 6.0, Docker] I tried to summarize the Docker environment construction and commands necessary to create a portfolio
I tried to summarize personally useful apps and development tools (development tools)
I tried to build the environment of WSL2 + Docker + VSCode
I tried to summarize personally useful apps and development tools (Apps)
I tried to explain what you can do in a popular language for web development from a beginner's point of view.
I tried to make it possible to set the delay for the UDP client of Android by myself
I will explain the nesting of for statements that kill beginners
I tried to solve the problem of "multi-stage selection" with Ruby
I tried to summarize the words that I often see in docker-compose.yml
I want to create a chat screen for the Swift chat app!
[Swift5] How to get an array and the complement of arrays
I tried to build the environment of PlantUML Server with Docker
The story of Collectors.groupingBy that I want to keep for posterity
[Swift 5] Processing and precautions for transitioning to the iOS settings app
[Ruby] Tonight, I tried to summarize the loop processing [times, break ...]
Special Lecture on Multi-Scale Simulation: I tried to summarize the 5th
[Tips] How to solve problems with XCode and Swift for beginners
Special Lecture on Multi-Scale Simulation: I tried to summarize the 8th
I tried to check the operation of gRPC server with grpcurl
Special Lecture on Multi-Scale Simulation: I tried to summarize the 7th
I tried to solve the problem of Google Tech Dev Guide
I tried to summarize iOS 14 support
I tried to summarize Java learning (1)
I tried to summarize Java 8 now
I tried to summarize the stumbling points when developing an Android application
[Ruby] I want to extract only the value of the hash and only the key
[Introduction to Java] I tried to summarize the knowledge that I think is essential
Don't forget to summarize the features and points of the flea market apps
I want to pass the argument of Annotation and the argument of the calling method to aspect
I tried to make full use of the CPU core in Ruby
I tried to visualize the access of Lambda → Athena with AWS X-Ray
[Ruby] I tried to summarize the methods that frequently appear in paiza
[Ruby] I tried to summarize the methods that frequently appear in paiza ②
I translated the grammar of R and Java [Updated from time to time]
I want you to use Enum # name () for the Key of SharedPreference
(For beginners) Swift UI view element collection
I tried to summarize Java lambda expressions
I tried to implement the Iterator pattern