TienLe
TienLe

Reputation: 623

UIProgressView on UITableViewCell

I am using AFNetworking to download files from my server. It works fine. But I have one issue: My ProgressView updates wrong cell(UI, not data) when I scroll up or down. Here is my code:

My Cell:

AFHTTPRequestOperation *operation;
@property (weak, nonatomic) IBOutlet DACircularProgressView *daProgressView;


- (IBAction)pressDown:(id)sender {
AFAPIEngineer *apiEngineer = [[AFAPIEngineer alloc] initWithBaseURL:[NSURL URLWithString:AF_API_HOST]];
                operation = [apiEngineer downloadFile:(CustomObject*)object withCompleteBlock:^(id result) {
                    
                } errorBlock:^(NSError *error) {
                    
                }];
                
                __weak typeof(self) weakSelf = self;
                apiEngineer.afProgressBlock = ^(double progress, double byteRead, double totalByToRead) {
                    [weakSelf.daProgressView setProgress:progress animated:YES];                    
                };
}

- (void)setDataForCell:(id)object{
    
}

My table:

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
            CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])];
            cell.backgroundColor = [UIColor clearColor];
       
            CustomObject *aObject = [listObject objectAtIndex:indexPath.row];
            [cell setDataForCell: aObject];
            
            return cell;
        
}

My downloadHelper:

- (AFDownloadRequestOperation*)downloadFile:(CustomObject*)aObject
                          withCompleteBlock:(AFResultCompleteBlock)completeBlock
                                 errorBlock:(AFResultErrorBlock)errorBlock{
        
    NSString *link = URL;
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:link]];
  
    NSString *path = [NSString databasePathWithPathComponent:[NSString stringWithFormat:@"%@.zip", @"noname"]];
    AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:path shouldResume:YES];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        completeBlock(responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        errorBlock(error);
    }];
    
    [operation setProgressiveDownloadProgressBlock:^(AFDownloadRequestOperation *operation, NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {
        float progressF = (float)totalBytesReadForFile / totalBytesExpectedToReadForFile;
        self.afProgressBlock(progressF, totalBytesReadForFile, totalBytesExpectedToReadForFile);
    }];
    [operation start];
   
    return operation;
}

When I press button 'Download' in first cell: enter image description here

When I scroll down then scroll up, It is second cell: enter image description here

So, my question is: How to update UIProgressView on UITableViewCell? What is wrong with my code?

Upvotes: 0

Views: 1914

Answers (2)

Aaron
Aaron

Reputation: 7145

I have a small project on github that basically does what you want with a different style progress indicator. I built it a while back and it uses exactly the approach Rob mentions: The table and its cells are backed by a "model" that tells cells at a given index what their state should be. I also make sure cells are reused. It may be of some use to you:

https://github.com/chefnobody/StreamingDownloadTest

Upvotes: 4

Rob
Rob

Reputation: 437492

Because cells are re-used, you simply can't just keep a weak reference to the cell and update the cell's progress view. At the very least, rather than using a weak reference to the table view cell, you would use the index path, look up the correct cell using [tableView cellForRowAtIndexPath:indexPath] (this is a UITableView method that identifies the cell associated with a particular NSIndexPath and should not to be confused with the similarly named UITableViewDataSource method in which we do our cell dequeueing/configuring logic), and update that cell's progress view (assuming that cell is even visible).

Frankly, even that is dangerous, as it assumes that it's impossible to add or remove cells while downloads are in progress. (And that is obviously not a reasonable assumption/constraint to place upon an app.) Quite frankly, every time one wants to update download progress, one really should go back to the model, lookup the correct NSIndexPath based for the model object in question, and then use the above cellForRowAtIndexPath pattern to find and then update the cell in question.

Note, that suggests that the initiation of the download request may not be initiated by the table view cell. Personally, I maintain a model object that consists of an array of files to download, and associate the download queue with that, rather than the cell (though the progress handler would obviously update the cell). In short, you want a loosely coupled relationship between the UI (e.g. the cell) and the model driving the downloads.

Note, many naive implementations consider all of the above points and decide to abandon cell reuse in order to simplify the problem. I won't belabor the point, but I think that's a fundamentally misguided approach. In my opinion, one should have a proper model for the downloads separate from the view objects.

Upvotes: 6

Related Questions