UITableVIew scrolling fluency optimization

Reasons that affect the fluency of UITableView UITableView scrolling:

1. Too many calculations in the proxy method take up the time of the UI thread

2. Same as above

3. The organization of the view in the Cell is complicated. For example, using a layer will not have much impact, but if the layer uses transparency, or rounded corners, deformation and other effects, it will affect the drawing speed.


Regarding the first point, you must first understand the calling order and timing of the tableview's proxy (here refers to the set of methods of datasource and delegate, the same below). For general applications there will be the following order:

1. Ask the agent for the number Of Rows.

2. Ask the agent for height For Row At Index Path for each row.

3. Ask the agent for the cell For Row At Index Path visible on the current screen. (The actual measurement shows that the mobile phone with a 4-inch screen will take the number displayed on the screen + 2, and the number of 3.5-inch screens is the same as that of a 4-inch screen, although the number of cells that can be displayed on a 3.5-inch screen is smaller than that of a 4-inch screen!)

4. Then the cell is displayed.

tableView:heightForRowAtIndexPath:

Many people put the focus of optimization on the method of cell for row at indexpath, and try to calculate as little as possible here, but they ignore another method that can easily improve loading time:

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

     Table View will ask for the height of all cells every time it reloads data! This means that you have a hundred rows of cells, just like the agent wants the height of each cell 100 times, not the height of the number of cells currently displayed on the screen! Although there are more ways to calculate the cell height under iOS 7, reducing the time for calculating the height will greatly improve the speed of loading the Table View!

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath(**iOS 7专用**)

But someone said, I heard it from others, try not to call the reloadData method, I use insertRowsAtIndexPaths:withRowAnimation: to insert a new row, delete also use delete, this is the head office, right? !

In this way, the importance of the callback height For Row At Index Path cannot be ignored .  Because this callback method for all rows also needs to be called after each row is inserted or deleted  ! Yes all OK! You read that right, all you need to do is simply reduce the calculation of a proxy method, and the loading speed can be significantly improved.

For improving the calculation amount of tableView:heightForRowAtIndexPath:, it is to make the calculation complexity of this method as O(1) as possible, that is, simply take a value from the array and return it! Maybe someone will ask again, my application has a dynamic height, just like Weibo, with a variable number of texts, and maybe pictures, and the size is not fixed, how do these return to a fixed height? The fixed height I mean is not the fixed height of the row, but the time to get the height in the tableView:heightForRowAtIndexPath: callback is almost fixed.

For the calculation of the height, there is still a small detail to pay attention to, that is, if the height of the row is constant, then delete the tableView:heightForRowAtIndexPath: method in the proxy, set the rowHeight property of the Table View , and the similar numberOfRowsInSection: series of methods, I will not write them all. According to Apple's documentation, this can also reduce the call time.

Now back to business. For the cell whose height is not fixed, the traditional method is to write a class method for calculating the row height for the cell, pass in those dynamic elements (text, picture, etc.), and then return the calculated height. Call this method in tableView:heightForRowAtIndexPath:, fill in the required parameters to calculate the cell height. Of course, there is no problem, but if the amount of calculation is very complicated, every time you reloadData, it will take rowCount * single row height evaluation calculation time to calculate the row height. Think about 100 rows, and you need to reloadData or insert (delete) row from time to time. . . . The solution is to:

Use "space for time"

Advance the time to calculate the row height to when the data is retrieved from the server, and write the height back to the database after calculating the height. Don’t tell me that you are processing network requests in a blocking manner in the main thread. . . . Facing the wall and thinking about it, don't waste the youth of GCD and NSOperationQueue. The first thing that comes to mind is the classmate of NSThread, which proves that you are old. . . Now almost most of the multi-threaded operations do not need to use NSThread and runloop.

tableView:cellForRowAtIndexPath:

After talking about the optimization of calculating the cell row height, let's talk about the optimization of the tableView:cellForRowAtIndexPath: callback. The optimization idea is the same as above, and the calculation time in this callback is also reduced through preprocessing. This callback focuses on the optimization of asynchronous loading of images.

The asynchronous loading of pictures is nothing more than initiating an asynchronous request in this method. After the picture is loaded, set the picture according to the reference of UIImageView. Experienced programmers may use  lazy loading  to reduce the lag caused by too frequent network requests and switching threads to display pictures when sliding quickly. There is another problem here. The size of the returned image must be different from the last displayed one. Sometimes I am lazy and directly set the contentMode attribute of the image view to  compress the image view itself . This is a very tricky method, but it will also have a  non-negligible  impact on the scrolling speed of the table view. To deform a picture, you need to transform the picture. Every time you compress a picture, you need to multiply the picture by a transformation matrix. If you have a lot of pictures, the amount of calculation is not negligible.

Optimization suggestion: After uploading pictures from the Internet, cut them into appropriate size pictures according to the size of the pictures to be displayed, and only display the processed pictures each time, and display the big picture when viewing the big picture. It would be better if the server can directly return the preprocessed small image and the size of the image.

Manual Drawing view improves fluency

The manual drawing method is not to directly subclass UITableViewCell, and then override the drawRect: method, because there is not only one content view in the cell. If you don't know the hierarchical structure of the cell, you can use Reveal to see it.

It is not recommended to use UIView to draw cells, and it is recommended to use CALayer. The drawing of UIView is based on CoreGraphic and uses CPU. CALayer uses Core Animation, CPU and GPU take it all, and the system decides which one to use. The drawing of View uses bottom-up layer-by-layer drawing, and then rendering. Layer deals with Texture, using GPU's Texture Cache and independent floating-point computing unit to accelerate texture processing.

The problem has been solved: using the drawing command beginning with CG in UIView's drawRect is just a pseudo-off-screen rendering triggered, and the drawing is still drawn in the main thread synchronously by the CPU, and the off-screen rendering triggered in the layer will trigger the real off-screen rendering, which is drawn in an independent process.

Guess you like

Origin blog.csdn.net/enuola/article/details/41942963