Hassan Hussein
Hassan Hussein

Reputation: 157

Performing an heavy calculation in cellForRowAtIndexPath

I have a table view loaded with products. The price for each needs some heavy calculation so I would like to this outside the main UI thread because otherwise scrolling the table is very very jumpy almost impossible. no matter what I try I cannot get the syntax and the concept right. My current code does load the price on the background but when I scroll some cells are given prices which do not belong to them. I am not sure how to debug this. Any suggestions appreciated.

- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([self.products count] == 0) {
        UITableViewCell *cell = [[UITableViewCell alloc] init];
        return cell;
    }

    AvailableCustomerProductTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    cell.accessoryType = UITableViewCellAccessoryNone;

    cell.buttonAdd.tag = indexPath.row;
    [cell.buttonAdd addTarget: self action: @selector(addToSelectedProduct:) forControlEvents: UIControlEventTouchUpInside];

    Product *prod = nil;
    if (theTableView == self.searchDisplayController.searchResultsTableView) {
        prod = (Product *)[self.filteredProducts objectAtIndex:indexPath.row];
    } else {
        prod = (Product *)[self.products objectAtIndex:indexPath.row];
    }

    if (prod != nil) {
        cell.pNumber.text = prod.number;
        cell.description.text = prod.desc;

        if ([Common getProductPromotion:prod] != nil)  {
            cell.btnPromotionTag.hidden = NO;
            cell.btnPromotionTag.tag = indexPath.row;
            [cell.btnPromotionTag addTarget: self action: @selector(showPromotionDetails:) forControlEvents: UIControlEventTouchUpInside];
        }
        else{
            cell.btnPromotionTag.hidden = YES;
        }

        //Get the customer product price, first:
        //If if the product has a record in the productCustomerPrices list
        //if not get the price from the standard price.
        if (self.order.orderOrderCustomer != nil) {
            // Do the Customer price fetching in a background thread
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                CustomerPrice *custPrice = [Common getPriceForCustomer:self.order.orderOrderCustomer.customerId forProduct:prod.productId inManagedObjectContext:self.backgroundContext];

                // Return to the main thread to update the UI
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (custPrice) {
                        NSManagedObjectID *objId = custPrice.objectID;
                        CustomerPrice *custPrice = (CustomerPrice*)[self.managedObjectContext objectWithID:objId];

                        if (custPrice) {
                            // Check that the relevant data is still required and get the correct cell (it might have changed)
                            AvailableCustomerProductTableViewCell * correctCell = nil;
                            if ([[theTableView indexPathsForVisibleRows] containsObject:indexPath]) {
                                correctCell = (AvailableCustomerProductTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath];
                            }

                            [correctCell.btnPrice setTitle:[Common getCurrencyFormattedStringFromFloat:[custPrice.price floatValue]] forState:UIControlStateNormal];
                            [correctCell.btnPrice setTitleColor:[UIColor colorWithRed:0.01 green:0.65 blue:0.77 alpha:1] forState:UIControlStateNormal];
                            correctCell.btnPrice.enabled = NO;
                            [correctCell setNeedsLayout];
                        }                    }
                });
            });
        }
    }

    UISwipeGestureRecognizer* sgr = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(cellSwiped:)];
    [sgr setDirection:UISwipeGestureRecognizerDirectionRight];
    [cell addGestureRecognizer:sgr];

    return cell;
}

Upvotes: 0

Views: 237

Answers (1)

cdescours
cdescours

Reputation: 6022

Have you tried to do this in several steps :

  1. As you're loading the view (in viewWillAppear:)Load your products list to display the ListView quickly,
  2. make a server call in background to retrieve the prices list in a separate NSDictionary*prices with productId for key. When the calculation is over, you should call [self.tableView reloadData] on the main thread !
  3. In the cellForRowAtIndexPath: method, then you should only check if you have the price in you prices dictionary and display your button. Be sure to hide the button if you have no price, to avoid display glitches when the cell is recycled.

Like this, you avoid some complex reload by line and association causing probably your problems.

Upvotes: 1

Related Questions