xcode UICollectionViewCell 标签被重复使用并被错误放置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14968505/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 02:48:38  来源:igfitidea点击:

UICollectionViewCell label is reused and placed wrongly

objective-cxcodeuilabeluicollectionviewuicollectionviewcell

提问by jerik

I have a UICollectionView with a prototype cell. The cells loads and image and shows an label. As the cells have different sizes, they are changed via the CollectionViewFlowLayout. That works fine.

我有一个带有原型单元格的 UICollectionView。单元格加载并显示图像并显示标签。由于单元格具有不同的大小,它们通过 CollectionViewFlowLayout 进行更改。这很好用。

When I scroll the view in the Simulator, the labels seems to be reused and added wrongly on the images. How do I ensure that this do not happen and an image has only one label on the collectionview?

当我在模拟器中滚动视图时,标签似乎被重用并错误地添加到图像上。如何确保不会发生这种情况并且图像在 collectionview 上只有一个标签?

screenshot of issue

问题截图

UICollectionView

用户界面集合视图

#pragma mark - Collection view 
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    self.Data = [NSArray arrayWithObjects:@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, nil];
    return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.magazineLayout.count;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    int item = [indexPath row];

    mCell.backgroundColor = [UIColor lightGrayColor];

    // Set Image
    UIImage *image;
    image = [UIImage imageNamed:@"testimage.png"];
    mCell.imageView.image = image; 


    // Set Label
    NSString *title = [[NSString alloc] initWithFormat:@"Image %@", self.Data[item]];
    [mCell addSubview:[self cellTitle:title indexPath:indexPath]];

    return mCell; 
}

// Title will be reused and placed wrongly on pictures !
-(UILabel *)cellTitle:(NSString *)name indexPath:(NSIndexPath *)indexPath {

    CGSize itemSize = [self collectionView:self.collectionView layout:self.collectionView.collectionViewLayout sizeForItemAtIndexPath:indexPath];
    int top = itemSize.height - 40;
    int width = itemSize.width;

    UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0, top, width, 40)];
    title.textColor = [UIColor blackColor];
    title.text = name;
    title.backgroundColor = [UIColor whiteColor];
    title.alpha = 0.5f;

    return title; 
}

Edit: Workaround Solution

编辑:解决方法

viewWithTagworked fine, but I could not reposition the label. Sadly as I think this would be the best way. Here my workaround without viewWithTag:

viewWithTag工作正常,但我无法重新定位标签。可悲的是,我认为这将是最好的方法。这里我的解决方法没有viewWithTag

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    mCell.backgroundColor = [UIColor lightGrayColor];

    // Set Image
    UIImage *image;
    image = [UIImage imageNamed:@"testimage.png"];
    mCell.imageView.image = image; 

    [self cellTitleAndBackground:mCell indexPath:indexPath];

    return mCell; 
}


-(void)cellTitleAndBackground:(MagazineCell *)mCell indexPath:(NSIndexPath *)indexPath {

    // Get title
    NSString *name = [[NSString alloc] initWithFormat:@"Image %@", self.Data[indexPath.row]];

    // Get current cell size
    CGSize itemSize = [self collectionView:self.collectionView layout:self.collectionView.collectionViewLayout sizeForItemAtIndexPath:indexPath];
    int top = itemSize.height - 40;
    int width = itemSize.width;

    // Create title background
    UILabel *titleBackground = [[UILabel alloc] initWithFrame:CGRectMake(0, top, width, 40)];
    titleBackground.backgroundColor = [UIColor blackColor];
    titleBackground.alpha = 0.2f;
    titleBackground.tag = 70;
    [self removeReusedLabel:mCell tag:70]; 
    [mCell addSubview:titleBackground];

    // Create titleLabel
    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, top-8, width, 40)];
    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.font = [UIFont boldSystemFontOfSize:14];
    titleLabel.text = name;
    titleLabel.backgroundColor = [UIColor clearColor];
    titleLabel.tag = 72;
    [self removeReusedLabel:mCell tag:72];
    [mCell addSubview:titleLabel];
}

-(void)removeReusedLabel:(MagazineCell *)mCell tag:(int)tag {
    UILabel *foundLabelBackground = (UILabel *)[mCell viewWithTag:tag];
    if (foundLabelBackground) [foundLabelBackground removeFromSuperview];
}

cheers -- jerik

干杯——杰瑞克

回答by rdelmar

This is happening because of cell reuse. If you're going to add the label like this, you should check to see if there is one on the cell you get from the dequeue method, and remove it if it's present. You could give the label a tag, and then use viewWithTag: to see if a label is present, and then have it call removeFromSuperview. I haven't tested this, but I think something like this should work:

这是由于单元重用而发生的。如果您要添加这样的标签,您应该检查从出队方法获得的单元格上是否有标签,如果存在则将其删除。您可以给标签一个标签,然后使用 viewWithTag: 查看标签是否存在,然后让它调用 removeFromSuperview。我还没有测试过这个,但我认为这样的事情应该有效:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    UILabel *foundLabel = [mCell viewWithTag:47];
    if (foundLabel) [foundLabel removeFromSuperview];
    .......

Remember to set the tag of the labels to the same number when you create them in the cellTitle:indexPath method. BTW, you should be adding this label to the cell's contentView instead of the cell itself.

在 cellTitle:indexPath 方法中创建标签时,请记住将标签的标签设置为相同的数字。顺便说一句,您应该将此标签添加到单元格的 contentView 而不是单元格本身。

After Edit:

编辑后:

This modification of your code worked fine for me, using viewWithTag:

使用 viewWithTag 对您的代码进行的这种修改对我来说效果很好:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGSize cellSize = [self.magazineLayout[indexPath.item] size];
    return cellSize;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"RDCell" forIndexPath:indexPath];
    mCell.backgroundColor = [UIColor lightGrayColor];
    if ([mCell viewWithTag:47]) [[mCell viewWithTag:47] removeFromSuperview];
    UIImage *image = self.magazineLayout[indexPath.item];
    mCell.imageView.image = image;

    NSString *title = [[NSString alloc] initWithFormat:@"Image %@", self.data[indexPath.item]];
    [mCell addSubview:[self cellTitle:title indexPath:indexPath]];

    return mCell;
}


-(UILabel *)cellTitle:(NSString *)name indexPath:(NSIndexPath *)indexPath {
    CGSize itemSize = [self.magazineLayout[indexPath.item] size];
    int top = itemSize.height - 40;
    int width = itemSize.width;
    UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0, top, width, 40)];
    title.textColor = [UIColor blackColor];
    title.text = name;
    title.backgroundColor = [UIColor whiteColor];
    title.alpha = 0.5f;
    title.tag = 47;
    return title;
}

In this example, the array, magazineLayout, was filled with 16 pictures of different sizes.

在这个例子中,数组 magazineLayout 填充了 16 张不同大小的图片。

The easier way though, is to add the labels to your custom cell in the storyboard (or xib). If you have it pinned to the sides and bottom, and have its height set to 40, you'll get what you want with no code to create the label or check for its presence.

不过,更简单的方法是将标签添加到故事板(或 xib)中的自定义单元格。如果您将其固定在侧面和底部,并将其高度设置为 40,您将获得所需的内容,无需代码来创建标签或检查其是否存在。

回答by Jugs

I know the answer has been accepted and closed. But just in case someone stumble across this thread, the right approach to avoid this problem is to subclass UICollectionViewCell and then use

我知道答案已被接受并关闭。但以防万一有人偶然发现这个线程,避免这个问题的正确方法是继承 UICollectionViewCell 然后使用

[self.collectionView registerClass:[LPCollectionViewCell class] forCellWithReuseIdentifier:cellIdentifier];

Initialize all your elements in the initWithFrame method of the subclass

在子类的 initWithFrame 方法中初始化所有元素

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        self.lblName = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 95.0, 106.0, 20)];
        self.lblName.text = @"";
        self.lblName.textAlignment = NSTextAlignmentCenter;
        [self.lblName setFont:[UIFont fontWithName:@"GothamRounded-Bold" size:10]];
        [self.lblName setTextColor:[UIColor colorWithRed:137.0f/255.0 green:137.0f/255.0 blue:137.0f/255.0 alpha:1.0]];
        [self.lblName setBackgroundColor:[UIColor clearColor]];
        [self addSubview:self.lblName];
}

Make the elements public so that it can be accessed later in the collection view data source calls.

将元素设为公开,以便稍后在集合视图数据源调用中可以访问它。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    LPCollectionViewCell *cell = (LPCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    [cell.lblName setText:@"This is awesome"];
}

And thats it!

就是这样!