简体   繁体   中英

iOS UITableView reusable cell slow when showing all of them

I have a table view which has 10000+ cells. and there is a segment button (All/Favorite) on the top.

this is the call back for the segment:

- (IBAction)call_segment:(id)sender {

    [self.tableView beginUpdates];
    [self.tableView reloadData];
    [self.tableView endUpdates];
}

for favorite page, even when there are no favorite items, I simply set the cell height to be 0. But in this way, I created all 10000+ cells on screen.

if 'all' is selected, the table works just fine since cells have normal height and only some of them are dequeued on screen.

Here is my code:

//if it's not in favorite, just hide it by setting the height to be 0

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self isFavorite]) {
        int uniqueId = [self uniqueIdWithIndexPath:indexPath];
        if ([DATABASE isFavoriteWithMode:self.mode uniqueId:uniqueId] == NO) {
            return 0;
        }
    }
    return 60;
}

//in table view datasource: //I think the problem is, when setting the height to be 0, all the cells are allocated. I set the cell to be hidden but still takes memory. any way to deal with it?

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    BOOL isFavorite = [DATABASE isFavoriteWithMode:self.mode uniqueId:[self uniqueIdWithIndexPath:indexPath]];

    if ([self isFavorite] && isFavorite == NO) {
        cell.hidden = YES;
        return [[UITableViewCell alloc] init];

    }
    else {
        cell.hidden = NO;
        ListCell *cell = (ListCell *)[tableView dequeueReusableCellWithIdentifier:CELL_LIST];

        Datum *datum = [DATABASE datumWithMode:self.mode uniqueId:[self uniqueIdWithIndexPath:indexPath]];


        BOOL isRead = [DATABASE isReadWithMode:self.mode uniqueId:[self uniqueIdWithIndexPath:indexPath]];


        cell.indexLabel.text = [NSString stringWithFormat:@"%d", datum.uniqueId];
        cell.titleLabel.text = [NSString stringWithFormat:@"%@", datum.q];


        return cell;
    }

}

Note: I dont wanna just show the favorite cells, since the logic is way too complex. I am using sqlite, but i dont think database performance is the problem, since the 'all' tab works just fine.

The reason i wanted to just set the height to be 0 is the simple implementation of cell numbers

- (BOOL)isFavorite {
    return self.segment.selectedSegmentIndex == 1;
}


- (IBAction)call_segment:(id)sender {

    [self.tableView beginUpdates];
    [self.tableView reloadData];
    [self.tableView endUpdates];
}

#define NUM_SECTIONS 15

- (int)numRows {
    return [DATABASE numberOfDataForModes:self.mode];
}

- (int)numSections {
    if ([self numRows] % NUM_SECTIONS > 0) {
        int numSections = [self numRows] / [self numRowsPerSection];
        if ([self numRows] % [self numRowsPerSection] > 0) {
            numSections++;
        }
        return numSections;
    }
    return NUM_SECTIONS;
}

- (int)numRowsPerSection {
    return [self numRows] / NUM_SECTIONS;
}

- (int)numRowsInLastSection {
    if ([self numRows] % ([self numSections] - 1) > 0) {
        return [self numRows] % ([self numSections] - 1);
    }
    else {
        return [self numRowsPerSection];
    }
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    int start = section * [self numRowsPerSection] + 1;
    int end = start + [self numRowsPerSection] - 1;
    if (end > [self numRows]) {
        end = [self numRows];
    }
    return [NSString stringWithFormat:@"From %d to %d", start, end];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    NSMutableArray *titles = [NSMutableArray arrayWithCapacity:[self numSections]];
    int start = 1;
    while (start < [self numRows]) {

        NSString *title = [NSString stringWithFormat:@"%d", start];
        [titles addObject:title];

        start += [self numRowsPerSection];
    }

    return titles;

}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return index;
}

- (int)uniqueIdWithIndexPath:(NSIndexPath *)indexPath {
    int uniqueId = indexPath.row + 1 + indexPath.section * [self numRowsPerSection];
    return uniqueId;
}

- (NSIndexPath *)indexPathWithUniqueId: (int)uniqueId {
    int section = (uniqueId - 1) / [self numRowsPerSection];
    int row = uniqueId - 1 - [self numRowsPerSection] * section;

    return [NSIndexPath indexPathForRow:row inSection:section];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self isFavorite]) {
        int uniqueId = [self uniqueIdWithIndexPath:indexPath];
        if ([DATABASE isFavoriteWithMode:self.mode uniqueId:uniqueId] == NO) {
            return 0;
        }
    }
    return 60;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section == [self numSections] - 1) {
        return [self numRowsInLastSection];
    }
    return [self numRowsPerSection];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [self numSections];
}

Instead of hiding the cells why dont you just return 0 from the datasource method

 – tableView:numberOfRowsInSection: 

You can just make use of the isFavorite value within this function and return 0 if there it is NO.

You got it already. The problem is the size of 0 of non-favorite cells. That contradicts the idea of reusabel cells. You will have thousands of cells on the screen, although invisible but existing and therefore resource consuming. Better think of a smarter way of doing that. Your data source delegate (view controller I guess) should only return the number of non-fav cells and therefore cellForRowAtIndexPath should only provide those cells of non-fav items. Plus cellForRowAtIndexPath should actually reuse the cells which I do not see in your code sniplet.

No matter how much you try having 10,000 views onscreen is not going to be the solution to your problem. You need to change your code structure such that you can return 0 for the tableView:numberOfRowsInSection: delegate when the favourites tab is chosen.

Any other 'solution' is an attempt to hack an alternative together, but this will not work and is bad code practice anyway. Implement it properly, by responding to the delegates properly.

I've given up making both table section separated. the logic is way too complicated.

I guess there is no way to save memory even when you hide the cells. Thank you guys for your input. you are all correct.

It's actually not that bad since favorite table are typically not that long. just one section with all entries.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM