Reusing blocks on iOS
DivKit reuses blocks during the following events:
- Setting new DivData.
- Scrolling elements in complex containers:
gallery,grid,tabs.
Reuse when new DivData is set
DivKit always attempts to reuse already created elements. For this purpose, the reuse method of the UIViewRenderable protocol is used, which determines whether a block can be reused.
Reuse process diagram
Below is a diagram of the block reuse process in iOS:
The reuse process includes the following steps:
- Check if there is a view for reuse
- Check the
isBestViewForReusemethod to determine if the view is the best candidate for reuse - If the view is not the best candidate, check the
canConfigureBlockViewmethod - If the view can be reused, update its configuration
- If the view cannot be reused, create a new view
- Configure the view and return it
Implementation details of reuse methods
In the block reuse process in DivKit, two key methods are used: canConfigureBlockView and isBestViewForReuse:
- canConfigureBlockView - a basic method that checks the UIView type. Its implementation is usually very simple:
// Example for ImageBlock
public func canConfigureBlockView(_ view: BlockView) -> Bool {
view is RemoteImageViewContainer
}
- isBestViewForReuse - for simple blocks (Image, Text) and some composite blocks (Container, Gallery), by default, it simply calls
canConfigureBlockView:
// Default implementation in the UIViewRenderable protocol
public func isBestViewForReuse(_ view: BlockView) -> Bool {
canConfigureBlockView(view)
}
- isBestViewForReuse for some complex blocks - for certain composite blocks (e.g., DecoratingBlock), it has more complex logic:
// Example for DecoratingBlock
func isBestViewForReuse(_ view: BlockView) -> Bool {
guard let view = view as? DecoratingView,
view.childView != nil else { return false }
return child.isBestViewForReuse(view.childView!)
}
In this example, isBestViewForReuse not only checks the view type but also:
- Requires the presence of a non-empty child view
- Recursively checks if the child view is optimal for reuse
- Features of composite blocks - blocks containing collections of child elements (Container, Gallery) delegate the reuse of child elements to a special
reusedmethod for arrays:
// In ContainerBlockView
blockViews = blockViews.reused(
with: model.children.map(\.content),
attachTo: self,
observer: observer,
overscrollDelegate: overscrollDelegate,
renderingDelegate: renderingDelegate
)
The reused method itself implements a two-level reuse system for each child element.
Reuse in complex containers
In complex containers such as gallery, grid, and tabs, DivKit uses the UIKit collection cell reuse mechanism. Each block has a reuseId property that is used to identify blocks that can be reused.
For example, in GalleryView, the cellForItemAt method uses block.reuseId to get a reusable cell from the collection:
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: block.reuseId,
for: indexPath
) as! CellType
By default, reuseId returns the string "DefaultReuseId", but blocks can override this property for more precise control over reuse.
Using the reuseId property
For more efficient reuse of blocks in complex containers, it is recommended to set the reuseId property for blocks with the same structure. This will allow DivKit to reuse views more efficiently.
Example:
{
"type": "gallery",
"items": [
{
"type": "container",
"reuse_id": "video_block",
"items": [
{
"type": "video",
...
}
]
},
{
"type": "container",
"reuse_id": "image_block",
"items": [
{
"type": "image",
...
}
]
},
{
"type": "container",
"reuse_id": "image_block",
"items": [
{
"type": "image",
...
}
]
},
{
"type": "container",
"reuse_id": "video_block",
"items": [
{
"type": "video",
...
}
]
}
]
}
In this example, blocks with the same reuse_id will be reused among themselves. This is especially important for heavy objects such as video to avoid unnecessary recreation.
Learn more
You can discuss topics of interest in the DivKit user community in Telegram: https://t.me/divkit_community_en.