- Interface smoothness and responsiveness is one of the main advantages of iOS. However, from time to time the UI gets overloaded so much that the devices are no more able to cope with that issue, the framerate drops below 60fps, and we literally have a hard time watching smooth animation. In this particular article, I will talk about the causes and ways to combat the described problem.
How is 60 FPS achieved? There is a simple answer to that! CPU and GPU perform a number of calculations and output a new frame every 16.6 ms. When one of them can’t deal with the job anymore – here the framerate drops come! Thus, our task is to simplify their work as much as possible.
Let’s begin with the basic part. In order to unload the CPU, it is necessary to unload the main thread, which runs rendering. The most common bottleneck is UITableView / UICollectionView dataSource methods:
These methods will be called during cell preparing before each displaying, so it’s better to get rid of cumbersome calculations inside these methods and rather leave only the return of data prepared in advance.
Get rid of the on-the-fly calculations, it will be much more efficient to make the necessary ones before displaying; save them and use the result. In addition, it will be possible to parallelize calculations on multiple threads.
If you support iOS 10 or newer, pay attention to the protocol UITableViewDataSourcePrefetching / UICollectionViewDataSourcePrefetching, its essence is to asynchronously prepare the cells in advance.
Starting from iOS 8, the height/cell size can be calculated automatically, but I don’t recommend doing so. Thing is, the calculation will be made with the help of autolayout, so the system will have to solve a system of linear equations, and the more elements you have, the more complicated the equation will be. It’s way better to make all the calculations manually by simply adding heights and indents. Try to minimize the amount of UIView. For example, developers often create a cell with all possible UI elements and hide/show the required ones depending on the data model.
Firstly, UIView creation and rendering is a resource-intensive process by itself, and secondly, the work with hiding/displaying hidden elements can be avoided.
It will be more efficient to create a cell for certain sets of UI elements. However, keep in mind that UITableView / UICollectionView will keep in memory only a limited number of cells for re-use, so try not to overdo it.
Such a separation will be especially effective, if cells contain UITableView / UICollectionView.
In addition, UI should be created in a code rather than in .xib / .storyboard, so that you don’t waste time loading nib which makes UIView creation faster.
- If the cells have UIImageView, then use small-sized images.
- Make sure to perform read / write operations like saving a downloaded image not in the main thread.
Now let’s switch to the process of image formation. The interface in iOS consists of UIView, each of which has a CALayer, which is essentially a two-dimensional array of RGBA values (red, green, blue, alpha). In order to display an image, the CPU and GPU must calculate the RGB values for each pixel based on the location of all UIViews. (Next step is colour calculation, i.e. rendering).
Supposedly, we have two overlapping UIViews, both of which have colours with alpha equal to 1. Then, in order to calculate the colour at the intersection point, the system will simply take the colour of the one above. C = R
What if aplha <1? The system will have to complicate the equation. C = G + R * (1-α).
The more variables the equation contains, the more complicated it’s going to be. In addition, UIVisualEffectView might turn out to be one of the UIView, and that will definitely complicate the calculation even more.
Therefore, it’s preferable to use UIVIew (colours) with alpha equal to 1 in cases when we are forced to optimize the calculation time. You can find areas with colour mixing using the “Color Blended Layers” simulator tool.
Red colour indicates the areas where colour mixing occurs, areas without colour mixing are marked with a green one. This screenshot shows that the part of my UIView in the backgroundColor is set to clearColor, which means the system performs unnecessary calculations.
Speaking of colour mixing, UIView.opaque should be mentioned as well. This property determines the system’s expectation as to whether UIView is transparent and affects the level of optimization while rendering. For example, for opaque UIView with opaque equal to 1, the redrawing logic isn’t called for UIView placed under. (Property is ignored if colour alpha! = 1)
The necessity to make a smooth image also affects the amount of calculations while rendering. CALayer pixels do not necessarily correspond to the position of physical ones on the screen, for example, one of them can get directly between two physical pixels. In this case, the system will perform anti-aliasing to create the illusion of having all the CALayer pixels̆ in the final image. This mechanism can be called, for example, by using fractional numbers when positioning UIView, or, for example, by setting the 60×61 image in UIImageView with 60×60 frame.
Anti-aliasing zones can be found with the help of “Сolor Misaligned Layers” simulator tool.
It’s essential to understand what Offscreen Rendering is when you’re working with rendering optimization. The point of this rendering approach is that the system renders a certain layer using CPU + RAM instead of GPU + VRAM.
This rendering type might call the following:
- UIView -drawRect:
- Drawing with CGContext
The CPU will take some load off GPU using Offscreen Rendering, if you set a CALayer’s shouldRasterize property to YES, so the certain layer will be rendered with the help of CPU. Conversely, free up the CPU resources, offloading workflows to the GPU, for example, with the usage of shadow image in the.png / .jpg format instead of CALayer.shadowWidth.
Offscreen Rendering zones can also be found with the help of “Color Offscreen Rendered” simulator tool.
It’s better to work with this type of rendering mainly just to unload the GPU, for example, if you support the iPhone4 and the power of its GPU is not enough to run 60 fps.
The table will certainly be working at 60 FPS if you apply the described optimizations. As for the iPhone 5S and newer models, dataSourse optimization methods will suffice to achieve an outstanding performance. However, understanding the basics of rendering will be useful to any developer.
At this final point, I want to thank you for your attention, hope you’ll consider some of the mentioned tips and apply them in practice.