質問

Within our app we have a free magazine that users can download in PDF format. If they have not downloaded an issue, that UITableViewCell image has a low alpha so that the user can see that it is not downloaded.

If you tap a cell it will start to download using an AFHTTPRequestOperation and once complete you can view the PDF using QuickLook.

The problem I am having is, when the user initiates the download, then scrolls away and then back, the UITableViewCell that they tapped somehow loses reference that it was downloading and therefore doesn't update the UIProgressView or change the alpha to 1.0 when the download is finished. I cannot for the life of me figure out why [[tableView indexPathForCell:cell] isEqual:indexPath] is not equaling:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    PUCViewpointItem *item = [self.items objectAtIndex:indexPath.row];

    // Check to see if we are already on the row that is activated
    if (indexPath.row == self.selectedIndexPath.row) {

        // File Path
        NSString *path = [self itemPath:item];

        // Should we read the issue
        if (item.isDownloaded && path) {
            item.downloadPath = path;
            [self readIssue:item];
            return;
        }

        // TableView Cell
        PUCViewpointTableViewCell *cell = (PUCViewpointTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];

        if (!path) {

            Utility *utility = [[Utility alloc] init];
            NSURLRequest *request = [NSURLRequest requestWithURL:item.url];
            AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
            NSString *localPath = [[utility localDirectory] stringByAppendingFormat:@"/%@.pdf", item.name];
            operation.outputStream = [NSOutputStream outputStreamToFileAtPath:localPath append:NO];
            [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {

                float totalProgress = (float)totalBytesRead/totalBytesExpectedToRead;
                if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
                    cell.progressView.hidden = NO;
                    cell.progressView.progress = totalProgress;
                    item.isDownloading = YES;
                    item.isDownloaded = NO;
                    item.progress = totalProgress;
                }

            }];
            [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
                if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
                    cell.fullImage.alpha = 1.0f;
                    cell.progressView.hidden = YES;
                    item.isDownloaded = YES;
                    item.isDownloading = NO;
                }
                NSLog(@"%d == %d", [tableView indexPathForCell:cell].row, indexPath.row);
            } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
                    cell.progressView.hidden = YES;
                    item.isDownloading = NO;
                    item.isDownloaded = NO;
                }
            }];
            [operation start];

        }

        return;
    }

    NSIndexPath *oldIndexPath = self.selectedIndexPath;
    self.selectedIndexPath = indexPath;
    [tableView beginUpdates];
    [tableView endUpdates];

    // Which way are we scrolling?
    UITableViewScrollPosition position;
    if (indexPath.row == 0 || (oldIndexPath && oldIndexPath.row < indexPath.row)) {
        position = UITableViewScrollPositionTop;
    } else {
        position = UITableViewScrollPositionBottom;
    }

    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:position animated:YES];

}

If I start a download, then scroll down in my tableView, my NSLog statement will log something like 3 == 10 which makes no sense.

Any ideas how I can fix this?

役に立ちましたか?

解決

I was curious about your case so I wrote a little test project, you can find it here: https://github.com/mrojas/MRTableViewTest

Basically, the way to solve it was:

  • Put the logic to download an item, in the item class
  • Make the cell be the delegate of the item, to be notified about progress/completion
  • When cells are scrolled (reused), setting the item on them is enough. They figure the current status and set themselves to be delegates.

Check the project, try it, and let me know if you have doubts.

I didn't use AFNetworking but instead simulated some task that takes 10 seconds to complete, in 2 seconds interval.

他のヒント

I think you have to store somewhere (like an NSMutableArray instance variable) the indexPath you are downloading. so you can do something like that :

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
        if( [indexArray indexOfObject:indexPath] != NSNotFound )
        {
            // alpha for downloading
        }    
    }

In your AFNetworking completion blocks you should remove this indexPath from you indexArray.

As comments said, cells are not reliable for storing any kind of information as they are reallocated when you scroll

   @interface ViewController ()
    {
        NSMutableArray *_indexArray;
    }

    @end

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        UITableView *table = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
        table.delegate = self;
        table.dataSource = self;
        _indexArray = [@[] mutableCopy];
        [self.view addSubview:table];
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
    {
        return 50;
    }

// called when you scroll to new cells or when reloadData is called
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
    {
        UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"default"];
        cell.textLabel.textColor = [UIColor blueColor];

        // red color if I'm downloading, else blue
        if ([_indexArray indexOfObject:indexPath] != NSNotFound) {
            cell.textLabel.textColor = [UIColor redColor];
        }
        cell.textLabel.text = @"cell in the table";
        return cell;
    }

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
    {
        [_indexArray addObject:[indexPath copy]];
        NSLog(@"downloading...");
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        cell.textLabel.textColor = [UIColor redColor]; // update UI temporary (until scroll)

            // HD Image so I can scroll up and down for testing
        NSString *res = @"http://res.cloudinary.com/******/image/upload/*****/Motorola_Razr_HD_Cam_Sample_7_ftzrj0.jpg";
            // custom class for download
        [[PLSApi api] downloadDataAtURL:[NSURL URLWithString:res] withBlock:^(NSData *data) {
            // download complete
            [_indexArray removeObject:indexPath];
            cell.textLabel.textColor = [UIColor blueColor]; // update UI
            NSLog(@"finished...");
        }];
    }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top