UICollectionView - 在其他单元格中重复的子视图实例

Posted

技术标签:

【中文标题】UICollectionView - 在其他单元格中重复的子视图实例【英文标题】:UICollectionView - Instance of subview duplicated in other cells 【发布时间】:2013-08-16 15:25:47 【问题描述】:

首先,我的应用程序的粗略描述是一个特定组织的杂志库应用程序

    我有一个定制的 UICollectionViewCell 和一个故事板原型。原型中有 3 个视图 - UIImageViewUILabelUILabel

    另外在collectionView:cellForItemAtIndexPath:中,我创建了一个

    UIButton (action:@selector(downloadButtonTapped:))

    还有一个

    UIProgressView 其中 tag=indexPath.itemhidden=YES。然后我将 UIProgressView 添加到一个数组 - self.progressViewArray 与单元格的 indexPath.item 位于 same index .

    每次点击特定单元格的“下载”按钮并调用 downloadButtonTapped: 时,我都会得到 cellindexPath sender 的 superview 的 superview,即 cell。然后我检查[self.progressViewArray[indexPath.item] tag] == indexPath.item。如果满足条件,则执行 [self.progressViewArray[indexPath.item] setHidden:NO]

我有一个包含 22 个对象的数据源,因此我有 22 个单元格。到目前为止,一切都像我想要的那样工作。即使我将单元格滚动出视图(出队)并返回视图(回收),UIProgressView 仍然存在。

问题:

progressView 也重新出现在其他单元格中!例如。如果我点击单元格 3 的“下载”按钮,UIProgressView 会正确显示在该单元格中但是,单元格 10 和 19 也显示“重复的”UIProgressView。这个问题也适用于其他细胞。例如。当我点击单元格 20 的“下载”时,我向上滚动并看到单元格 12 和 4 具有相同的加载 UIProgressViews,就好像这些杂志也在被下载一样。

之所以提到same是因为我有一个setDownloadProgressBlock,它会更新 UIProgressViews 的进度以及单元格 3、10 和 19 UIProgressViews 的进度一起进行。

我真的希望我能在这里找到一些帮助,因为我已经为这个问题绞尽脑汁了很多天。

附注编辑调试尝试如下。


以下是涉及的方法:

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];
    

感谢您抽出宝贵的时间。我很感激...

编辑:

作为另一次调试尝试,我在return cell; 之前做了一个NSLog(@"%@ at indexPath #%d", self.progressViewArray[indexPath.item], indexPath.item);,只是为了确认只有被点击的单元格的 UIProgressView 受到影响(setHidden=NO)而且确实如此。 p>

点击了单元格 3 的下载按钮滚动到底部回到 collectionView 的顶部像往常一样看到好像单元格 10 和 19 的进度视图也处于活动状态,这就是我记录的内容。如您所见,当下载处于活动状态时,日志显示只有单元格 3 的 progressView (tag=3) 为 (setHidden=NO)。单元格 10 和 19 仍然隐藏/不受影响。但我不明白为什么我会在那里看到一个活跃的 progressView。

这是日志:

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

【问题讨论】:

【参考方案1】:

经过 2 周的努力,终于解决了这个问题。简单的解决方案,但我之前没有想到。

我有一个属性如下@property (strong, nonatomic) NSMutableArray *progressViewsArray;。不要忘记分配并初始化它。我是在viewDidLoad 做的。

collectionView:cellForItemAtIndexPath:中,我在返回单元格之前使用了以下逻辑。

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];
    

downloadButtonTapped: 中,这就是我创建 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];

在 AFNetworking 的 setDownloadProgressBlock (downloadButtonTapped:) 中:

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

最后在 AFNetworking 的 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.

希望这对未来的人有所帮助,干杯!

【讨论】:

你真的应该在你的细胞子类的 prepareForReuse 中这样做。【参考方案2】:

您应该将所有视图添加到原型单元格中,并将其中一些设置为隐藏。具体来说,在collectionView:cellForItemAtIndexPath: 中,您应该设置您配置的所有视图的hidden 属性,以便在需要时使其可见并在不需要时隐藏它们。那么不管单元格是新的还是重复使用的,您始终保证正确的视图是可见的。

在您当前的解决方案中,您有一些逻辑可以防止创建新的进度视图(如果您已经有一个)。但是,您永远不会真正从单元格中删除任何进度视图。因此,当您重用单元格时,它可能仍然具有作为子视图的进度视图。使用隐藏或显示视图(并设置进度值)的方法可以避免这种情况。

【讨论】:

嗨@Wain 感谢您的回复。我没有在原型单元格中包含 UIProgressView 是因为我意识到我需要将每个 UIProgressView 的标记分配给 indexPath.item 以便我可以识别要 unhide 的 UIProgressView。在防止创建新 UIProgressViews 的逻辑中,它做了我想要的 - 当我点击的单元格滚动关闭视图并重新打开时,相同的 UIProgressView 仍然存在并正在加载。问题是当我什至没有说明这些单元格的 indexPath 时,相同的 UIProgressView 也出现在其他单元格上。我在这里做错了什么?谢谢。【参考方案3】:

我也有类似的问题。我解决了它。你已经在这个方法中更改了单元格的子视图:- (IBAction)downloadButtonTapped:(id)sender,而你没有将这个子视图更新为方法:collectionView:cellForItemAtIndexPath:。当重用单元工作时,它只是从这个方法中获取视图:collectionView:cellForItemAtIndexPath:,并且不包括在这个方法之外更改的子视图,所以你应该在方法collectionView:cellForItemAtIndexPath: 中做一些事情来更新你对子视图的更改细胞。无论如何,我希望这可以帮助你。

【讨论】:

抱歉,我没有更新collectionView:cellForItemAtIndexPath: 中单元格的子视图,我不明白您的意思。 [cell.contentView addSubview:self.progressViewArray[indexPath.item]];? 我以为我已经做到了

以上是关于UICollectionView - 在其他单元格中重复的子视图实例的主要内容,如果未能解决你的问题,请参考以下文章

如果从 UICollectionView 的其他部分中选择了一个单元格,则取消选择所有其他选择

UICollectionView 点击选择多个单元格

UICollectionView,单元格选择不起作用,单元格滚动正常

修改 UICollectionView 中特定单元格中的 UIButton

如何使 UICollectionView 单元格可移动?

为 uicollectionView 单元格设置比例大小?