UIKit rendering - CATransaction
On the previous post, we discovered a mysterious
CATransaction.commit at the bottom of our interrupted layout phase stack. Let’s find out its role in the layout process.
CATransaction is little used explicitly. We find few usages of it in our projects at Fabernovel:
Apple’s documentation tells us that the
CATransaction class has two main methods:
commit. To understand how they work, let’s define a method that can block the main thread for a given time:
Usually changes made to a view tree are not effective immediately. They are simply programmed. Indeed, if we block the execution of our code, just after having applied a modification to a tree, it is not immediately visible:
On the other hand, if we surround our modification by the begin and commit methods of CATransaction, the screen refreshes immediately:
Therefore a transaction transfers a view hierarchy to the render server that updates the screen.
Apple states it in its documentation:
Any modification made to the view tree must be part of a transaction
In our first example when we changed the color of our sample view, where was the transaction?
CoreAnimation started one for us. It watches each change that requires a screen refresh (probably with well-placed KVO) and starts a transaction if none is present. We define this transaction as “implicit”.
Implicit transactions are created automatically when the layer tree is modified by a thread without an active transaction
To reveal it, we can use a feature of the transactions: they are nestable. When one transaction is nested into another, the commit of the “child” transaction is effective only at the commit of the parent.
Therefore, if we make two modifications: one, isolated, and the other, encapsulated in an explicit transaction, we find ourselves in the same case as our first example: the screen displays our modification only after three seconds.
The culprit? The implicit transaction. Our transaction is encapsulated by the implicit transaction created by
sampleView.backgroundColor = .red is executed. The modifications will be sent to render server only once the implicit transaction commits them. Our view becomes green only after three seconds.
CATransaction and layout
We can easily verify that a transaction triggers a layout phase at commit time by modifying our
This action method lays out the current view hierarchy. It is, in a way, similar to a
When you think about it, it’s quite logical that the commit of a transaction is followed by a layout phase. The role of a transaction is to send the current state of the view tree to the render server. It seems that it lays out the view tree before sending it.
So we come to a first element of answer: looking for the next layout phase is looking for the commit of the current implicit transaction.
Indeed, in the stack of our initial code, we do find a call for a transaction commit:
And it is an implicit transaction! The modification of the sample view constraint is the origin of it, we did nothing explicitly.
CATransaction and completionBlock
It is possible to be notified once a transaction completes. This is particularly interesting when a transaction encapsulates animations. The block is only executed once all the programmed animations are completed.
Here is an example from one of our project at Fabernovel:
Here, without any animation, our block is executed on the main queue right after the commit method so once the table view has finished reloading.
In the next post, we will tackle the run loops and what is its link to the transaction commit of our interrupted layout phase stack.