TableView: cellForRowAtIndexPath: getting a call not only for visible cells?

I have a tableView with sections that could be opened and closed. So, when I click on a section to open it, it is populated with cells, and -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) gets as many times as I pointed in -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section .

It is right? Isn't that just the number of visible cells?

Because in my case I have a bad situation: I have many user cells (50-100 cells) and the call -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) for each cell slows down the opening of the section, calling each time when reading from nib is performed and the contents of the cell are filled with the image. I check the visibility of the cell inside -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) as follows:

 if ([[self.tableView indexPathsForVisibleRows] containsObject:indexPath]) NSLog(@"visible %@", indexPath); 

and this shows that out of 45 cells, only 6 or 7 is visible. Others are not visible. But cell creation is still in progress. Here is the code:

 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"IVCell"; IVCamera *camera = [server.cameras objectAtIndex:indexPath.row]; IVServerListViewCell *cell = (IVServerListViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { [[NSBundle mainBundle] loadNibNamed:@"IVServerListCell" owner:self options:nil]; cell = (IVServerListViewCell*)_tableViewCell; self.tableViewCell = nil; } [cell textLabel].text = camera.cameraName; cell.preview = camera.preview; cell.userData = camera; cell.isEnabled = (server.isInactive)?NO:camera.isOnline; return cell; } 

Is it correct? Or am I missing something?

+8
ios uitableview
source share
5 answers

Well, I somehow dealt with my problem. Here are my ideas and thoughts, how I came to a decision. Maybe it can be useful to someone.

I instructed the memory allocation and call stack using tools during the events of the opening section. He showed me that most of the time is spent loading a cell from a nib file.

Firstly, I did this by reducing the size of the nib file , i.e. minimizing the number of views used in the user tableview cell (now its only 2 views and 2 labels instead of 6 views, 2 images and 2 labels before). This gave me some improvement in cell loading. Apple documentation suggests using as few views as possible and not using transparency. Therefore, be attentive to these offers.

Secondly, as I discovered earlier, that not all cells are visible that are created -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) , I decided to somehow reduce the number of downloads of new cells from the nib file. To achieve this, I came up with a simple idea: return empty cells by default for invisible rows, and load custom cells from nib for visible ones. Here is the code snippet:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if ([self index:indexPath isInvisibleInTableView:tableView]) return [self getBlankCellForTableView:tableView]; // the rest of the method is the same ... } -(BOOL)index:(NSIndexPath*)indexPath isInvisibleInTableView:(UITableView*)tableView { NSMutableArray *visibleIndexPaths = [self getExtendedVisibleIndexPathsForTableView:tableView]; return ![visibleIndexPaths containsObject:indexPath]; } -(UITableViewCell*)getBlankCellForTableView:(UITableView*)tableView { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"IVBlankCell"]; if (!cell) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"IVBlankCell"] autorelease]; return cell; } 

As you can see, I do not use only the method -(NSArray*)indexPathsForVisibleRows tableview to detect visible cells. Instead, I wrote my own method -(NSMutableArray*)getExtendedVisibleIndexPathsForTableView:(UITableView*)tableView . This was necessary because for some reason when using -(NSArray*)indexPathsForVisibleRows cells that are next to the last visible cell or the cells preceding the first visible cell were created as empty cells and looked like empty cells when scrolling. To overcome this, in -(NSMutableArray*)getExtendedVisibleIndexPathsForTableView: (UITableView*)tableView I add boundary cells to the cells of the visible array:

 -(NSMutableArray*)getExtendedVisibleIndexPathsForTableView:(UITableView*)tableView{ NSArray *visibleIPs = [tableView indexPathsForVisibleRows]; if (!visibleIPs || ![visibleIPs count]) return [NSMutableArray array]; NSIndexPath *firstVisibleIP = [visibleIPs objectAtIndex:0]; NSIndexPath *lastVisibleIP = [visibleIPs objectAtIndex:[visibleIPs count]-1]; NSIndexPath *prevIndex = ([firstVisibleIP row])?[NSIndexPath indexPathForRow:[firstVisibleIP row]-1 inSection:[firstVisibleIP section]]:nil; NSIndexPath *nextIndex = [NSIndexPath indexPathForRow:[lastVisibleIP row]+1 inSection:[lastVisibleIP section]]; NSMutableArray *exVisibleIndexPaths = [NSMutableArray arrayWithArray:[tableView indexPathsForVisibleRows]]; if (prevIndex) [exVisibleIndexPaths addObject:prevIndex]; [exVisibleIndexPaths addObject:nextIndex]; return exVisibleIndexPaths; } 

Thus, I reduced the opening time of sections with a large number of user cells, which was proved by the tracing of tools and the feeling when working with the application.

+5
source share

check the size of the table. it may be that the height of your table is very large so that it continues to load cells until your cell fills the entire size of the table.

+2
source share

That seems right, yes. The idea of โ€‹โ€‹optimizing the load itself is how the "dequeueReusableCellWithIdentifier" works. if u loads the image from a remote location, it means that you will want to optimize the code. but not from loading cells, as it looks right here.

0
source share

I used a similar method, but since indexPathsForVisibleRows is sorted, you do not need to use containsObject. Instead, you can simply do:

 // // Checks if indexPath is visible in current scroll state, we are expanding bounds by 1 // because the cells that are next to the last one visible or the cells that are previous // to the first one visible could look empty while scrolling. // - (BOOL)isIndexPathVisible:(NSIndexPath *)indexPath { NSInteger row = [indexPath row]; NSArray *visible = [self.tableView indexPathsForVisibleRows]; NSInteger count = [visible count]; NSInteger first = (count > 0) ? MAX([visible[0] row] - 1, 0): 0; NSInteger last = (count > 1) ? [visible[1] row] + 1: first + 2; return row >= first && row <= last; } 

By the way; this assumes that you are using only one section. It will not work in more than one section.

0
source share

Adding another problem. If I discarded all the changes made to the cell.

 if (! self.cell) { self.cell = [[LanguageCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; self.cell.accessoryType = UITableViewCellAccessoryNone; } else { self.cell.checkImage.image = NO; } 
0
source share

All Articles