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.
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))
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.
I will explain the process of the life cycle of ViewController. From the conclusion, the processing proceeds like the image below.
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, 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.
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.
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.
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
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).
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 ()
.
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)
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.
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.
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.
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