Location>code7788 >text

Principles of UITableView - Exploration and Reimplementation Code

Popularity:559 ℃/2024-08-06 08:35:59

Reprinted from Jane's Books.Original addressThis article focuses on some specific details, like view reuse, the most basic principles can be viewed in the source code.


Previously re-implemented a list container view, due to Apple did not open source, here to share the process of exploring some of the details of UITableView.MPTableView: A list view like UITableView, more fast, more features.

1-The Elusive ContentOffset

When a UISrollview is sliding, it has to get its changing contentOffset value, either through its protocol or in its layoutSubviews, and the latter gets the offset value much more often - when sliding fast, the number of protocol callbacks is much lower than the number of layoutSubviews calls, i.e. the number of times the contentOffset is obtained is much lower, so that once it needs to be obtained based on the contentOffset, it is much lower. When sliding fast, the number of protocol callbacks of the scrollView is much lower than the number of layoutSubviews calls, i.e., the number of times the contentOffset is obtained is less, so once you need to do some precise work based on the contentOffset, then the effect will be worse.

Even if you choose the layoutSubviews that are most likely to be called, the number of times they are called doesn't always change linearly, the number of times layoutSubviews are called back is finite (n times 1s), so once a sharp slide is made, it's not going to be called back pixel-by-pixel, it's going to be called a certain number of times within the total slide distance, and the final contentOffset that you get each time is going to be in a jumpy.

2-About UIView view hierarchy

It was found that after setting the hierarchy of the child view by insertSubview: AtIndex: after addSubview, once the frame of the child view is re-modified, the index will be invalidated. But I found out that I can make a view stay at a higher or lower level in its parent view by elevating it.

This is a problem that needs to be solved when implementing section header/footer stop floating for tableview in plain mode. Because section view is a lot earlier than the cell to join the display area (header), then when scrolling to the header needs to float on the head of the cell (cover), then the cell will appear to cover the header. At first, we chose zPosition, but found that this destroys the original state of the view, and then directly bringToFront to realize plain section hovering.

3-update related

Multiple insert/delete/reload operations.

(1) It's better to consolidate all IndexSet/Array of some kind of operation into one array, so that there are only 3 arrays at most

(2) these three operations reload is prioritized, as for insert and delete, UIKit is to let delete first, then insert, in which reload is actually done first delete after insert operation.

(3) you can often see some apps to reload a cell to expand its content (cell's height becomes bigger) effect, if the bottom of the cell has a dividing line or other content, then in the process of expanding the cell, the animation effect is that the cell below the cell to move down, the height of the cell is expanded, but the dividing line does not move but appears directly at the bottom. But the dividing line does not move but appears directly at the bottom of the cell. This is due to the fact that we usually choose the None animation method for reloading, which is simply a view hidden.

(4) willInsertCell this protocol in the insert animation will be called back before, if the insert operation inside the None animation enumeration, then perhaps you can do something through this protocol to customize the effect of animation. I don't know if apple provides this mechanism.

4-Timing of UIScrollView's layoutSubviews call

A UIScrollView that performs setContentSize first and then addSubview will perform layoutSubviews multiple times, whereas a UIScrollView that puts setContentSize last will only perform layoutSubviews once.

5-UITableView's cell reuse restrictions

UITableView does not limit the number of reused cells, and during the test, all the cells that were scratched out of the screen were not destroyed. During this test, the height of each cell is incremented in order, which leads to a situation where the more you slide to the back, the fewer cells there are in the display area (compared to the area you slid through before), which leads to an incremental increase in the number of reused cells (because the number of cells displayed on the previous screen is much larger), and the number of these cells waiting to be reused will not be released. These cells waiting to be reused will not be released even though they are large in number.

6-Practical hidden.

UITableView on the slide out of the screen cell are hidden rather than remove, in the design of the cell reuse cache, found that once frequent cell remove operation will be 10% higher than the use of hidden cpu utilization (in the iPhone5 above), and UITableView seems to be an earlier version is remove now also changed to hidden. UITableView seems to be earlier version is remove now also changed to hidden.

In practice, if you don't have to destroy the UIView, then hidden is faster than remove. In addition, setting hidden to YES triggers a removeFromSuperView operation on its child view.

7 - Problems with xib and pure frame settings

xib loaded view and then set with frame, then if you change the parent view's frame will lead to its height to 0, then it is because xib loaded this view's default autoresizingMask have height related, set to none can be. This problem is very disturbing before, have not found this cat, of course, only under the specific conditions mentioned earlier (manually change the view frame, and its above the child view is loaded through xib) will appear.

8-cell correlation.

(1) dequeueReusableCellWithIdentifier:identifier function does not call does not help you achieve the cell does not reuse, only need to be initialized when the reuseIdentifier assigned to nil can be, here initWithFrame to create the Here initWithFrame created cell should be the default reuseIdentifier is nil, because UITableViewCell's NS_DESIGNATED_INITIALIZER is not in initWithFrame.

(2) UITableViewCell was selected by the trigger operation, in fact, not in the cell internal gesture click, but through the UITableView by obtaining the current position of the click to determine which cell is clicked, and then call the cell setSelected:animated function. This should be done to avoid coupling.

(3) UITableViewCell's selected highlighting. When the cell is selected, you will find that all the views above the cell, change to clearColor to make the cell's highlighted background color show up, and change back when the cell highlighting is canceled, then this problem arises. The background color of these subviews is moved! So where is the original background color being recorded again, and running out when it needs to be restored.

There are 2 thoughts here:

-Inherit UIColor to write a class that provides a property that is subject to the recorded color, and when setBackgroundColor encounters this class, use his recorded color to set the color. Every time you want to set all the subviews to clearColor, record the background color of all these subviews with this class.

-A container is used to store the corresponding background color of these views, and the key value corresponds to it, where the memory address of each UIView is used for the key.

-UITableView uses a private api of UIView to solve this problem: -[UIView _descendent:willMoveFromSuperview:toSuperview:].

The important point is that when willRemoveSubview is triggered on top of the cell, it will help the removed subview to return to its original background color if the current cell is selected and highlighted.

(4) UITableViewCell's layoutSubviews: Generally speaking, those default elements on top of the cell, titleLabel and delete button sliding left and right, are often not used. And once in the UITableViewCell subclass of layoutSubviews inside the super layoutSubviews, will also take care of these default views, the actual test will also make the cpu occupancy rate down a few percent.


MPTableView improvements to UITableView:

-provides method - (void)reloadDataAsyncWithCompletion:(void(^)(void))completion to load cell height asynchronously, selects gcd's global queue to compute and cache the height and then refreshes the tableview in the main thread. --During this process, if the tableview still has content to display, then during this asynchronous calculation process, you can still slide to browse the original content.

-fixed update animation, especially for UITableView plain mode, after multiple insert/delete/reload operations, the section header/footer will have a very strange movement (this also happens in Mac OX under the NSTable component). Also provides custom animation methods for insert/delete.

-You can choose to force the reload to continue to reuse the previously generated cell reuse object. every reload of UITableView will clear all the data, but very often, in fact, the cell is the same as the previous one, so every time you reload, undoubtedly more than deleting the old cell and creating a new process --while the old one can actually be used.

-Multi-way update operation support, you can open multiple update transactions at the same time/delayed, and set different time and animation state.

-With the same subview style, the cpu usage will definitely be lower than UITableView. Under the same update operation, the cpu usage is also lower. And also in cell dragging mode.

-as well as some other improvements and additions to the api, which can be used with newer api's on lower system versions.