Question

First of all, a rough description of my app is a Magazine Library app for a particular organisation.

  1. I have a customized UICollectionViewCell done up with a storyboard prototype. In the prototype are 3 views - UIImageView, UILabel, UILabel.
  2. Additionally in collectionView:cellForItemAtIndexPath:, I have created an instance of a

    • UIButton (action:@selector(downloadButtonTapped:))

    and a

    • UIProgressView where its tag=indexPath.item and hidden=YES. I then add the UIProgressView to an array - self.progressViewArray at the same index as the cell's indexPath.item.
  3. Each time the "Download" button for a particular cell is tapped and downloadButtonTapped: is called, I get the cell and indexPath of the sender's superview's superview which is the cell. I then check if [self.progressViewArray[indexPath.item] tag] == indexPath.item. If the condition is fulfilled, then [self.progressViewArray[indexPath.item] setHidden:NO] is executed.

I have a datasource of 22 objects, thus, I have 22 cells. Everything works like how I want it to so far. Even when I scroll the cell off view (dequeued) and back in view (recycled), the UIProgressView still remains.

Problem:

The progressView is also reappearing at other cells! E.g. if I tapped the "Download" button for cell 3, the UIProgressView correctly shows in that cell HOWEVER, cells 10 and 19 are also showing the "duplicated" UIProgressView. This problem also applies when to other cells. E.g. when I tap "Download" for cell 20, I scroll up and I see cells 12 and 4 with the same loading UIProgressViews too as if those magazines were being downloaded too.

Why I mentioned same is because I have a setDownloadProgressBlock that updates the progress of the UIProgressViews and the progress of cells 3, 10, and 19 UIProgressViews are progressing together.

I really hope that I can find some help here as I've been killing my brains for many days over this issue.

P.S. Edited debugging attempt below.


Here are the methods involved:

collectionView:cellForItemAtIndexPath:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"Cell";
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    // * ========== Configure Cell ========== *
    // Initialisation of UIImageViews and UILabels here.
    // ...

    // CREATION OF CELL'S DOWNLOADBUTTON
    UIButton *downloadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [downloadButton addTarget:self action:@selector(downloadButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    [downloadButton setFrame:CGRectMake(178, 197, 140, 44)];
    [downloadButton setTitle:[self setDownloadButtonTitleAtIndexPath:indexPath.item] forState:UIControlStateNormal];
    [downloadButton setTitle:@"Downloading..." forState:UIControlStateDisabled];
    [downloadButton setEnabled:YES];
    [downloadButton setUserInteractionEnabled:YES];
    [downloadButton setTag:indexPath.item];
    [cell.contentView addSubview:downloadButton];


    // CREATION OF CELL'S UIPROGRESSVIEW
    UIProgressView *progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
    [progressView setFrame:CGRectMake(20, 248, 150, 9)];
    [progressView setHidden:YES];
    [progressView setTag:indexPath.item];
    [self.progressViewArray addObject:progressView];

    // This chunk is to prevent creating new progress views when the cells are being recycled.
    if ([self.progressViewArray[indexPath.item] tag] != progressView.tag)
    {
        [self.progressViewArray replaceObjectAtIndex:indexPath.item withObject:progressView];
    }

    [cell.contentView addSubview:self.progressViewArray[indexPath.item]];
    // * ---------- Configure Cell ---------- *

    return  cell;
}

downloadButtonTapped:

- (IBAction)downloadButtonTapped:(id)sender
{
    // Get the indexPath of tapped row.
    UICollectionViewCell *cell = (UICollectionViewCell *)[[sender superview] superview];
    NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];

    // If file does not exist.
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath])
    {
        // SHOW THE CELL'S HIDDEN UIPROGRESSVIEW
        if ([self.progressViewArray[indexPath.item] tag] == indexPath.item)
        {
            [self.progressViewArray[indexPath.item] setHidden:NO];
        }


        // AFHTTPRequestionOperation code here...

        // Track download progress.
        [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
         {
             for (UIProgressView *progressView in self.progressViewArray)
             {
                 if (progressView.tag == indexPath.item)
                 {
                     [progressView setProgress:(float) totalBytesRead / totalBytesExpectedToRead];
                 }
             }
         }];

        // Post download codes etc ...

        [operation start];
    }
}

Thank you for your time people. I appreciate it...

Edit:

As another debugging attempt, I have done a NSLog(@"%@ at indexPath #%d", self.progressViewArray[indexPath.item], indexPath.item); just before return cell; just to confirm that only the tapped cell's UIProgressView is affected (setHidden=NO) and indeed it is.

I tapped cell 3's download button, scrolled to the bottom and back to the top of the collectionView, as usual seeing as if cell 10 and 19's progressViews are active too, and this is what I have logged. As you can see, while the download is active, the log shows that ONLY cell 3's progressView (tag=3) is (setHidden=NO). Cell 10 and 19's are still hidden / not affected. But I don't understand why I'm seeing an active progressView there.

Here's the log:

2013-08-17 10:59:33.179 SFCCA[22885:907] <UIProgressView: 0x1c5bca00; frame = (20 248; 150 9); hidden = YES; opaque = NO; layer = <CALayer: 0x1c5d1000>> at indexPath #0
2013-08-17 10:59:33.187 SFCCA[22885:907] <UIProgressView: 0x1c59c7c0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 1; layer = <CALayer: 0x1c5bb310>> at indexPath #1
2013-08-17 10:59:33.193 SFCCA[22885:907] <UIProgressView: 0x1c5aa1f0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 2; layer = <CALayer: 0x1c5aa270>> at indexPath #2
2013-08-17 10:59:33.201 SFCCA[22885:907] <UIProgressView: 0x1c5b9860; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 3; layer = <CALayer: 0x1c5b9a30>> at indexPath #3
2013-08-17 10:59:33.207 SFCCA[22885:907] <UIProgressView: 0x1c5aa670; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 4; layer = <CALayer: 0x1c5b8d60>> at indexPath #4
2013-08-17 10:59:33.214 SFCCA[22885:907] <UIProgressView: 0x1c5b51d0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 5; layer = <CALayer: 0x1c5b5270>> at indexPath #5

// Here, I have tapped cell 3's "Download" button.
// Notice that at the bottom of the log, when I have scrolled off cell 3 and back, the log shows that the progressView (tag=3) in cell 3 is hidden=NO
2013-08-17 10:59:43.389 SFCCA[22885:907] {downloadButtonTapped} - File does not exist. Download then read PDF.
2013-08-17 10:59:43.929 SFCCA[22885:907] <UIProgressView: 0x1c5d5a10; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 6; layer = <CALayer: 0x1c5b4420>> at indexPath #6
2013-08-17 10:59:43.938 SFCCA[22885:907] <UIProgressView: 0x1c5cd8e0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 7; layer = <CALayer: 0x1c5b68d0>> at indexPath #7
2013-08-17 10:59:44.216 SFCCA[22885:907] <UIProgressView: 0x1d8c3b90; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 8; layer = <CALayer: 0x1d8c3c30>> at indexPath #8
2013-08-17 10:59:44.223 SFCCA[22885:907] <UIProgressView: 0x1c5e08d0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 9; layer = <CALayer: 0x1c5b9330>> at indexPath #9
2013-08-17 10:59:44.600 SFCCA[22885:907] <UIProgressView: 0x1d8c9aa0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 10; layer = <CALayer: 0x1d8c9480>> at indexPath #10
2013-08-17 10:59:44.607 SFCCA[22885:907] <UIProgressView: 0x1d8cb590; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 11; layer = <CALayer: 0x1d8cb4b0>> at indexPath #11
2013-08-17 10:59:45.160 SFCCA[22885:907] <UIProgressView: 0x1d8b9e20; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 12; layer = <CALayer: 0x1d8a6140>> at indexPath #12
2013-08-17 10:59:45.171 SFCCA[22885:907] <UIProgressView: 0x1d8cc060; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 13; layer = <CALayer: 0x1d8ca870>> at indexPath #13
2013-08-17 10:59:45.813 SFCCA[22885:907] <UIProgressView: 0x1d88dc10; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 14; layer = <CALayer: 0x1d88dc90>> at indexPath #14
2013-08-17 10:59:45.822 SFCCA[22885:907] <UIProgressView: 0x1d8cc9e0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 15; layer = <CALayer: 0x1d872110>> at indexPath #15
2013-08-17 10:59:46.154 SFCCA[22885:907] <UIProgressView: 0x1c5ed300; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 16; layer = <CALayer: 0x1c5ed280>> at indexPath #16
2013-08-17 10:59:46.162 SFCCA[22885:907] <UIProgressView: 0x1c5eeae0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 17; layer = <CALayer: 0x1c5eea00>> at indexPath #17
2013-08-17 10:59:46.652 SFCCA[22885:907] <UIProgressView: 0x1c5ee0f0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 18; layer = <CALayer: 0x1c5e35f0>> at indexPath #18
2013-08-17 10:59:46.660 SFCCA[22885:907] <UIProgressView: 0x1c5efc70; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 19; layer = <CALayer: 0x1c5ee320>> at indexPath #19
2013-08-17 10:59:46.903 SFCCA[22885:907] <UIProgressView: 0x1c5e8b30; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 20; layer = <CALayer: 0x1c5e4aa0>> at indexPath #20
2013-08-17 10:59:46.914 SFCCA[22885:907] <UIProgressView: 0x1c5e8fd0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 21; layer = <CALayer: 0x1c5e7e50>> at indexPath #21
2013-08-17 10:59:48.583 SFCCA[22885:907] <UIProgressView: 0x1d88dc10; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 14; layer = <CALayer: 0x1d88dc90>> at indexPath #14
2013-08-17 10:59:48.590 SFCCA[22885:907] <UIProgressView: 0x1d8cc9e0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 15; layer = <CALayer: 0x1d872110>> at indexPath #15
2013-08-17 10:59:49.118 SFCCA[22885:907] <UIProgressView: 0x1d8b9e20; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 12; layer = <CALayer: 0x1d8a6140>> at indexPath #12
2013-08-17 10:59:49.127 SFCCA[22885:907] <UIProgressView: 0x1d8cc060; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 13; layer = <CALayer: 0x1d8ca870>> at indexPath #13
2013-08-17 10:59:49.730 SFCCA[22885:907] <UIProgressView: 0x1d8c9aa0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 10; layer = <CALayer: 0x1d8c9480>> at indexPath #10
2013-08-17 10:59:49.736 SFCCA[22885:907] <UIProgressView: 0x1d8cb590; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 11; layer = <CALayer: 0x1d8cb4b0>> at indexPath #11
2013-08-17 10:59:50.106 SFCCA[22885:907] <UIProgressView: 0x1d8c3b90; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 8; layer = <CALayer: 0x1d8c3c30>> at indexPath #8
2013-08-17 10:59:50.113 SFCCA[22885:907] <UIProgressView: 0x1c5e08d0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 9; layer = <CALayer: 0x1c5b9330>> at indexPath #9
2013-08-17 10:59:50.643 SFCCA[22885:907] <UIProgressView: 0x1c5d5a10; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 6; layer = <CALayer: 0x1c5b4420>> at indexPath #6
2013-08-17 10:59:50.649 SFCCA[22885:907] <UIProgressView: 0x1c5cd8e0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 7; layer = <CALayer: 0x1c5b68d0>> at indexPath #7
2013-08-17 10:59:50.967 SFCCA[22885:907] <UIProgressView: 0x1c5aa670; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 4; layer = <CALayer: 0x1c5b8d60>> at indexPath #4
2013-08-17 10:59:50.973 SFCCA[22885:907] <UIProgressView: 0x1c5b51d0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 5; layer = <CALayer: 0x1c5b5270>> at indexPath #5
2013-08-17 10:59:51.593 SFCCA[22885:907] <UIProgressView: 0x1c5aa1f0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 2; layer = <CALayer: 0x1c5aa270>> at indexPath #2
2013-08-17 10:59:51.598 SFCCA[22885:907] <UIProgressView: 0x1c5b9860; frame = (20 248; 150 9); opaque = NO; tag = 3; layer = <CALayer: 0x1c5b9a30>> at indexPath #3
2013-08-17 10:59:51.983 SFCCA[22885:907] <UIProgressView: 0x1c5bca00; frame = (20 248; 150 9); hidden = YES; opaque = NO; layer = <CALayer: 0x1c5d1000>> at indexPath #0
2013-08-17 10:59:51.995 SFCCA[22885:907] <UIProgressView: 0x1c59c7c0; frame = (20 248; 150 9); hidden = YES; opaque = NO; tag = 1; layer = <CALayer: 0x1c5bb310>> at indexPath #1
Was it helpful?

Solution

Managed to solve this problem after 2 weeks of struggling. Simple solution yet I didn't think of it earlier.

I have a property as follows @property (strong, nonatomic) NSMutableArray *progressViewsArray;. Don't forget to alloc and init it. I did it in viewDidLoad.

In collectionView:cellForItemAtIndexPath:, I used the following logic before returning the cell.

for (UIProgressView *progressView in cell.contentView.subviews)
{
    if ([progressView isKindOfClass:[UIProgressView class]])
    {
        [progressView removeFromSuperview];
    }
}
for (UIProgressView *progressView in self.progressViewsArray)
{
    if (progressView.tag == indexPath.item)
    {
        [cell.contentView addSubview:progressView];
    }
}

In downloadButtonTapped:, this is how I created an instance of a UIProgressView:

UIProgressView *progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
progressView.frame = CGRectMake(178, 180, 170, 9);
[progressView setTag:indexPath.item];
[cell.contentView addSubview:progressView];
[self.progressViewsArray addObject:progressView];

In AFNetworking's setDownloadProgressBlock (downloadButtonTapped:):

float progress = (float) totalBytesRead / totalBytesExpectedToRead;
[progressView setProgress:progress];

And finally in AFNetworking's setCompletionBlock (downloadButtonTapped:):

NSMutableArray *progressViewsToDelete = [[NSMutableArray alloc] init];
for (UIProgressView *pv in self.progressViewsArray)
{
    if (pv.tag == indexPath.item)
    {
        [progressViewsToDelete addObject:pv];
    }
}
if (progressViewsToDelete.count > 0)
{
    [self.progressViewsArray removeObjectsInArray:progressViewsToDelete];
}
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]]; // To reflect changes.

Hope this helps somebody in future, Cheers!

OTHER TIPS

You should add all of the views to the prototype cell and just set some of them to be hidden. Specifically though, in collectionView:cellForItemAtIndexPath: you should set the hidden attribute of all of the views that you configure, both to make them visible when required and to hide them when not required. Then it doesn't matter if the cell is new or reused, you always guarantee that the correct views are visible.

In your current solution, you have some logic to prevent the creation of new progress views if you already have one. But, you never actually remove any progress views from the cells. So when you reuse the cell it may still have a progress view as a subview. Using the method of hiding the view or showing it (and setting the progress value) would avoid this.

I have the similar problem. And I solved it. You have changed the subviews of the cell in this method:- (IBAction)downloadButtonTapped:(id)sender, and you didn't update this subview to the method:collectionView:cellForItemAtIndexPath:. When the reuse cells worked, it just fetch views from this method:collectionView:cellForItemAtIndexPath:, and that don't include the subviews changed outside this method, so you should do something in the method collectionView:cellForItemAtIndexPath: to update your changes about the subviews of the cell. I hope that this can help you anyway.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top