preface

In iOS development, it is very important to keep the interface smooth and good user experience, so the optimization of the interface is an old topic for us, here summarizes some information on the Internet and some of my own development experience, talk about some skills of interface performance.

UI is stuck and frames are dropped

VSync

CPU

  • Layout calculation, text calculation

View layout calculation is a key point of interface optimization. Generally, the view layout can be optimized by calculating the view layout in advance and caching the view layout. For example, the height of cell and the row height of label are related to calculation. For complex interfaces, using Autolayout will cause performance problems. The navigation library is based on Autolayout. Because too many constraints bring a very large amount of computing, to the CPU consumption is very large. Then this is easy to cause the CPU calculation time out of the lag. It would be much better to use a Frame. This article compares the performance impact of Frame and Autolayout. You can use the SDAutoLayout library, whose layout is Frame based, or use frameworks such as ComponentKit, AsyncDisplayKit, etc. Same Ulabel wide high and drawing method [NSAttributedString boundingRectWithSize: options: context:] and [NSAttributedString DrawWithRect: options: context:] is also can be placed in a background thread to avoid blocking the main thread.

  • Object creation

Object creation allocates memory, adjusts properties, and even reads files, which can be CPU intensive, so putting object creation into child threads can improve performance. Creating view objects in Storyboard also involves deserialization of files. Storyboards consume more resources than views created in code, so it is best to create and lay out high performance interfaces in code. If there are no touches involved, you can use a CALayer instead of a UIView, because a CALayer is much lighter than a UIView.

  • Text rendering

As you can see in the figure above, all the text content controls you can see on the screen, including UIWebView, are at the bottom of the page formatted and drawn as bitmaps through CoreText. Common text controls (UILabel, UITextView, etc.), its typesetting and drawing are carried out in the main thread, when the display of a large number of text, the CPU pressure will be very large. So you can customize the controls and use CoreText directly for layout control, but that would be a hassle, and I wouldn’t do that anyway, hahaha.

  • Image drawing

Image drawing consumes CPU resources. Put the drawing process in the background thread, and then set the result in layer contents in the main thread, which will improve THE efficiency of CPU. The code is as follows:

- (void)display { dispatch_async(backgroundQueue, ^{ CGContextRef ctx = CGBitmapContextCreate(...) ; CGImageRef img = CGBitmapContextCreateImage(ctx); CFRelease(ctx); dispatch_async(mainQueue, ^{ layer.contents = img; }); }); }Copy the code

Drawing an image is usually the process of drawing an image onto a canvas using those methods that begin with CG, and then creating a picture from the canvas and displaying it. As shown in the previous module diagram, CoreGraphic works on the CPU, so calling methods that start with CG consumes CPU resources. We can put the drawing process in the background thread and then set the result in layer contents in the main thread.

GPU

Compared to CPU, GPU can do a relatively simple thing: mainly texture (picture) and shape (triangle simulation vector graphics) two categories.

  • Texture rendering

All bitmaps, including images, text and rasterized content, are eventually committed from memory to video memory and bound to the GPU Texture. Both the process of submitting to video memory and the process of GPU adjusting and rendering Texture consume a lot of GPU resources. When a large number of images are displayed in a short period of time (such as when the TableView has a large number of images and slides quickly), the CPU usage is very low and the GPU usage is very high, and the interface will still drop frames.

  • Mixing of views

When multiple views (or Calayers) are displayed on top of each other, the GPU blends them together first. If the view structure is too complex, the mixing process can also consume a lot of GPU resources. To reduce GPU consumption in this situation, applications should minimize the number and level of views and indicate opaque attributes in opaque views to avoid useless Alpha channel composition. Of course, this can also be done by pre-rendering multiple views as a single image.

  • Off-screen rendering

CALayer’s border, rounded corners, shadows and masks, CASharpLayer’s vector graphics display, usually trigger offscreen rendering, which usually happens on the GPU. When a list view shows a large number of calayers with rounded corners and a quick swipe, you can observe that the GPU resource is full and the CPU resource consumption is low. The interface still slides normally, but the average number of frames drops to a very low level. ShouldRasterize to avoid this, try turning on the calayer.shouldrasterize property, but this will shift the off-screen rendering onto the CPU. For situations where only rounded corners are needed, you can also simulate the same visual effect by overlaying the original view with an already drawn rounded corner image. The most radical solution is to draw the graphics that need to be displayed as images in the background thread. Off-screen rendering is triggered when the following properties are set:

  • ShouldRasterize, rasterize

  • The layer mask, mask

  • AllowsGroupOpacity is YES, and the value of layer.opacity is less than 1.0

  • Layer. cornerRadius and set layer.masksToBounds to YES. This can be done using clipped images or layer drawings

  • Layer. shadows, (denoting related attributes beginning with shadow), uses shadowPath instead

The final