UItableView Cell中的图像是自动添加的吗?

7

大家好,

我这里遇到了一个非常奇怪的情况。我有一个表格视图,在其中我有自定义单元格。所以我设计了自己的自定义单元格。在自定义单元格中,我同时显示图像和文本。表格的数据来自服务器。如果JSON中有任何图像,则会显示图像,否则仅显示文本。因此单元格将是动态的。我使用bezier path将文本围绕图像包裹。现在如果有图像,则文本将被包裹。当我从服务器添加新帖子并刷新表格时,它会自动为其他帖子显示相同的图片。它将显示与上一张相同的图片。我不知道为什么新单元格会在其单元格中添加图像,即使代码运行良好,我已经使用断点进行了调试。请告诉我可能出现的问题。

这是cellForRowAtIndexPath的代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //define variables here
    static NSString *CellIdentifier = @"homeCell";
    HomeCell *cell = [tableView
                      dequeueReusableCellWithIdentifier:CellIdentifier
                      forIndexPath:indexPath];
    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
    if([arr_post count]>0)
    {
        NSMutableAttributedString *mutableAttributeStr;
        NSAttributedString *attributeStr;

        [cell layoutIfNeeded];
        //get the post data
        Post *user_post=[arr_post objectAtIndex:indexPath.row];

        //set the button tags
        cell.btn_like.tag=indexPath.row;
        cell.btn_comment.tag=indexPath.row;
        cell.btn_fav.tag=indexPath.row;
        cell.btn_con.tag=indexPath.row;
        cell.btn_book.tag=indexPath.row;

        //add info to buttons
        cell.btn_like.selected=user_post.isPostLiked;
        cell.btn_comment.selected=user_post.isPostCommented;
        cell.btn_fav.selected=user_post.isPostFavourite;
        cell.btn_con.selected=user_post.isPostCondolence;
        cell.btn_book.selected=user_post.isPostBookmarked;

        //add user info
        cell.label_name.text=user_post.username;
        cell.img_profile.layer.cornerRadius = 25;
        cell.img_profile.clipsToBounds = YES;
        [cell.img_profile setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.user_profileImage]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];
        //add location
        if([user_post.location isEqualToString:@"Not Available"])
        {
            [cell.img_icon_location setHidden:true];
            [cell.label_location setHidden:true];
        }
        else
        {
            [cell.img_icon_location setHidden:false];
            [cell.label_location setHidden:false];
            cell.label_location.text=user_post.location;
        }
        //ad post info
        cell.tv_post.text=user_post.post_description;
        cell.tv_post.font = [UIFont fontWithName:user_post.font_family size:[user_post.font_size floatValue]];
        [cell.tv_post setTextColor:[self colorFromHexString:user_post.font_color]];

        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        NSDate *date = [formatter dateFromString:user_post.modification_date];
        NSLog(@"user post image is %@",user_post.post_image);

        if([user_post.post_image isEqualToString:@"none"] && [user_post.post_video isEqualToString:@"none"])
        {

            NSLog(@"NOT INSIDE THE CONDITION");

        }
        else
        {
            NSLog(@"INSIDE BEIZER PATH CONDITION");
            UIBezierPath * imgRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, 10, 100, 100)];
            cell.tv_post.textContainer.exclusionPaths = @[imgRect];
            UIImageView *tv_image =[[UIImageView alloc]initWithFrame:CGRectMake(0, 10, 100, 100)];

            if(![user_post.post_image isEqualToString:@"none"])
            {
                [tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_image]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

            }
            if(![user_post.post_video isEqualToString:@"none"])
            {
                [tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_video_thumbnail]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

            }

            [cell.tv_post addSubview:tv_image];
        }
        //make textview height dynamic
        cell.tv_post.scrollEnabled=NO;
        if([user_post.post_image isEqualToString:@"none"] && [user_post.post_video isEqualToString:@"none"])
        {
            CGFloat fixedWidth = cell.tv_post.frame.size.width;
            CGSize newSize = [cell.tv_post sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
            CGRect newFrame = cell.tv_post.frame;
            newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
            cell.tv_post.frame = newFrame;
            cell.tv_height.constant=cell.tv_post.frame.size.height;
            [cell.view_tvContainer layoutIfNeeded];
        }
               //set the border of uiview
        cell.view_tvContainer.layer.borderColor = [UIColor blackColor].CGColor;
        cell.view_tvContainer.layer.borderWidth = 2.0f;

        //set the like count
        NSString *first_like_user=recent_like_name=user_post.recent_like_name;
        NSLog(@"FIRST LIEK USER IS %@",first_like_user);
        NSString *str_recent_like_name;
        int count=(int)[first_like_user length];
        int like_count=[user_post.like_count intValue];
        if(like_count>0)
        {
            cell.label_like_count.lineBreakMode=NSLineBreakByWordWrapping;
            [cell.label_like_count sizeToFit];
            [cell.label_like_count setHidden:false];
            NSString *str_like_count=[NSString stringWithFormat:@"%lu",(unsigned long)like_count-1];
            if(like_count==1)
            {
                if([myUsername isEqualToString:first_like_user])
                {
                    first_like_user=@"You like this post ";
                    count=3;
                }
                else
                {
                    first_like_user=[first_like_user stringByAppendingString:@" like this post"];
                }
            }
            else if(like_count==2)
            {
                if([first_like_user isEqualToString:myUsername])
                {
                    first_like_user=@"You";
                }
                Post *temp_user_post=[copy_arr_user_post objectAtIndex:indexPath.row];
                first_like_user=[first_like_user stringByAppendingString:@" and "];
                if(temp_user_post.recent_like_name==nil)
                {
                    temp_user_post.recent_like_name=@"";
                }
                str_recent_like_name=[temp_user_post.recent_like_name_two stringByAppendingString:@" like this post"];
                first_like_user=[first_like_user stringByAppendingString:str_recent_like_name];
            }
            else
            {
                if(like_count>1000)
                {
                    like_count=like_count/1000;
                    str_like_count=[NSString stringWithFormat:@"%lu",(unsigned long)like_count];
                    str_like_count=[str_like_count stringByAppendingString:@"k"];
                    first_like_user=[first_like_user stringByAppendingString:@" and "];
                    str_like_count=[str_like_count stringByAppendingString:@" others like this post"];
                    first_like_user=[first_like_user stringByAppendingString:str_like_count];
                }
                else
                {
                    if([first_like_user isEqualToString:myUsername])
                    {
                        first_like_user=@"You";
                    }
                    first_like_user=[first_like_user stringByAppendingString:@" and "];
                    str_like_count=[str_like_count stringByAppendingString:@" others like this post"];
                    first_like_user=[first_like_user stringByAppendingString:str_like_count];
                }
            }
            mutableAttributeStr = [[NSMutableAttributedString alloc]initWithString:first_like_user];
            attributeStr = [[NSAttributedString alloc]initWithString:@"\n" attributes:@{NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Bold" size:8]}];

            [mutableAttributeStr addAttribute:NSFontAttributeName value: [UIFont fontWithName:@"Helvetica-Bold" size:14.0]  range:NSMakeRange(0, count)];
            [mutableAttributeStr addAttribute:NSForegroundColorAttributeName value:[self colorFromHexString:@"#48a0dd"] range:NSMakeRange(0, count)];

            [mutableAttributeStr appendAttributedString:attributeStr];

            [cell.label_like_count setAttributedText:mutableAttributeStr];
        }
        else
        {
            [cell.label_like_count setHidden:true];


        }
        // show dynamic comment
        NSMutableArray *user_comments=user_post.comments;
        float comment_count=[user_post.comment_count intValue];
         NSLog(@"ID IS %@",user_post.id);
        if(comment_count>0)
        {
            //make label multiline
            cell.first_comment.lineBreakMode=NSLineBreakByWordWrapping;
            [cell.first_comment sizeToFit];
            cell.second_cmment.lineBreakMode=NSLineBreakByWordWrapping;
            [cell.second_cmment sizeToFit];
            cell.third_comment.lineBreakMode=NSLineBreakByWordWrapping;
            [cell.third_comment sizeToFit];

            if(comment_count==1)
            {
                [cell.first_comment setHidden:false];
                [cell.second_cmment setHidden:true];
                [cell.third_comment setHidden:true];

            }
            else if(comment_count==2)
            {
                [cell.first_comment setHidden:false];
                [cell.second_cmment setHidden:false];
                [cell.third_comment setHidden:true];

            }
            else
            {
                [cell.first_comment setHidden:false];
                [cell.second_cmment setHidden:false];
                [cell.third_comment setHidden:false];
                [cell.btn_more_comments setHidden:false];

            }
            for(l=0;l<[user_comments count];l++)
            {
                Comment *comment=[user_comments objectAtIndex:l];
                NSString *comment_string=[comment.user_name stringByAppendingString:@" "];
                comment_string=[comment_string stringByAppendingString:comment.comment];
                int count=(int)[comment.user_name length];
                NSMutableAttributedString* mutableAttributeStr = [[NSMutableAttributedString alloc]initWithString:comment_string];
                NSAttributedString *attributeStr = [[NSAttributedString alloc]initWithString:@"\n" attributes:@{NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Bold" size:8]}];

                [mutableAttributeStr addAttribute:NSFontAttributeName value: [UIFont fontWithName:@"Helvetica-Bold" size:14.0]  range:NSMakeRange(0, count)];
                [mutableAttributeStr addAttribute:NSForegroundColorAttributeName value:[self colorFromHexString:@"#48a0dd"] range:NSMakeRange(0, count)];

                [mutableAttributeStr appendAttributedString:attributeStr];
                // end of the repetitive pattern
                if (l == 0)
                {
                    [cell.first_comment setAttributedText:mutableAttributeStr];
                }
                else if (l == 1)
                {
                    [cell.second_cmment setAttributedText:mutableAttributeStr];
                }
                else if (l == 2)
                {
                    [cell.third_comment setAttributedText:mutableAttributeStr];
                }
            }
        }
        else
        {
            [cell.first_comment setHidden:true];
            [cell.second_cmment setHidden:true];
            [cell.third_comment setHidden:true];
            [cell.btn_more_comments removeFromSuperview];
        }
        cell.label_time.text=[BaseController timeAgoStringFromDate:date];
        [arr_indexpath addObject:indexPath];

    }

    return cell;
}

单元格对象会被重复使用。在重复使用单元格之前,您需要确保删除任何旧图像。 - Paulw11
我该怎么做?我已经寻找了一个多星期,但人们一直在说“单元格重用”、“单元格重用”、“单元格重用”,但无法找到任何解决方案,更不用说对问题的良好概述。你能告诉我最好的做法吗? - TechChain
请问您能否提供给我代码,这样我就可以更好地理解了。我不是一名有经验的开发者,刚刚开始学习iOS。 - TechChain
为了让文本环绕图片,我使用贝塞尔路径,因此我添加了一个图像视图,这就是我想要实现文本环绕图片所必须做的。 - TechChain
你应该在你的单元格中像标签一样定义在storyboard中的imageview。 - Paulw11
显示剩余3条评论
8个回答

1
请记住,单元格是可重用的。如果您的表数据源中有10000行,则表视图不会创建那么多行。它只会创建足够在屏幕上显示的行以及预加载的一些其他行。
每次滚动表时,表视图将使用那些变得不可见的单元格来加载刚刚变得可见的新单元格,从而制造出无限数量的行的假象。这就是为什么这些新单元格具有旧数据的原因。所以基本上,如果没有图像可用,在cellForRowAtIndexpath:中必须将UIImageView的图像设置为nil。
if([user_post.post_image isEqualToString:@"none"] && [user_post.post_video isEqualToString:@"none"])
        {

            NSLog(@"NOT INSIDE THE CONDITION");

        }

在你上面的代码中,你只是简单地打印出一个日志,而你还需要做的是将 UIImageView 的图像设置为 nil
所以这是我的建议:
//if([user_post.post_image isEqualToString:@"none"] && [user_post.post_video isEqualToString:@"none"])
//{

//   NSLog(@"NOT INSIDE THE CONDITION");

//}
//else
{
    NSLog(@"INSIDE BEIZER PATH CONDITION");
    UIBezierPath * imgRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, 10, 100, 100)];
    cell.tv_post.textContainer.exclusionPaths = @[imgRect];
    UIImageView *tv_image =[[UIImageView alloc]initWithFrame:CGRectMake(0, 10, 100, 100)];

    if(![user_post.post_image isEqualToString:@"none"])
    {
        [tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_image]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

    }
    else{
        [tv_image setImage:nil]; //Prevent the old data to be shown
    }

    if(![user_post.post_video isEqualToString:@"none"])
    {
        [tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_video_thumbnail]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

    }
    else{
        [tv_image setImage:nil];//Prevent the old data to be shown
    }

    [cell.tv_post addSubview:tv_image];
}

也许这段代码并不符合您关于数据显示的要求,但希望它能帮助您理解思路!

0

当需要显示表格视图单元时,您将创建一个新的单元或重用先前创建但不再在屏幕上可见的单元。由于您已经创建了一个 UIImageView 并将其添加到单元格中,因此当您重新使用它时,图像仍然存在。

为了防止这种情况发生,您需要保留对图像视图的引用,以便下一次能够将其删除。由于您正在动态添加它们并且没有 IBOutlet,因此最快的选择是为图像分配一个 tag

UIImageView *tv_image =[[UIImageView alloc]initWithFrame:CGRectMake(0, 10, 100, 100)];
tv_image.tag = 21; // arbitrary number but must be unique to the image

通过给图像打标签,下一次您就可以访问它:

UIImageView *tv_image = (UIImageView*)[cell viewWithTag:21];
if (tv_image != nil) {
    tv_image.image = nil; // remove the image (or remove the image view from the cell etc)
}

请确保相应地重构代码,以便如果您从标签中找到图像,则无需重新创建它等。


将我发布的第二个代码块放在获取单元格(HomeCell *cell = ...)之后,以清除图像。然后,在创建tv_image时应用标记。如果tv_image不为nil,则可以跳过initWithFrame和tag等步骤,因为它将使用已回收的版本。 - davbryn

0

你的 img_profile 重复出现是因为你在重复使用单元格。

请替换下面这行代码

[cell.img_profile setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.user_profileImage]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

使用以下代码:

    if (user_post.user_profileImage.length == 0) {
        cell.img_profile.hidden = true;
    }else{
        [cell.img_profile setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.user_profileImage]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];
    }

说明: 检查您的JSON是否包含用户个人资料图片,如果有则设置图片,否则隐藏个人资料图片视图。


0
一个表视图会重用单元格。这意味着如果您向单元格添加子视图,就像在[cell.tv_post addSubview:tv_image]这一行中所做的那样,那么当单元格被重用时,该子视图仍将存在于单元格中
基本上,如果您在tableView:cellForRowAtIndexPath:中向自定义单元格添加子视图,则可能会犯错误。
只需在第一次设计HomeCell时将tv_image视图设计到其中。 HomeView应该有一个名为tv_image的插座,连接到故事板或XIB中的图像视图。当您需要tv_image时,请显示它。否则,隐藏它。
    if([user_post.post_image isEqualToString:@"none"] && [user_post.post_video isEqualToString:@"none"])
    {

        NSLog(@"NOT INSIDE THE CONDITION");
        cell.tv_image.image = nil;
        cell.tv_image.hidden = YES;
        cell.tv_post.textContainer.exclusionPaths = @[];

    }
    else
    {
        NSLog(@"INSIDE BEIZER PATH CONDITION");
        UIBezierPath * imgRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, 10, 100, 100)];
        cell.tv_post.textContainer.exclusionPaths = @[imgRect];

        if(![user_post.post_image isEqualToString:@"none"])
        {
            [cell.tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_image]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

        }
        if(![user_post.post_video isEqualToString:@"none"])
        {
            [cell.tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_video_thumbnail]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

        }

        cell.tv_image.hidden = NO;
    }

此外,请确保 setImageWithURL:placeholderImage: 在加载图像时不会将图像视图的 hidden = NO

如何从每个新的TextView中删除贝塞尔路径。您可以在此处看到我还将贝塞尔路径添加到TextView中,因此当图像被删除时,必须删除贝塞尔路径。 - TechChain
我无法从故事板设计,因为我正在使用贝塞尔路径,所以我想在这里将文本环绕在图像周围。 - TechChain
然后在你的HomeCell代码中创建图像视图(并将其添加为子视图),并将其存储在属性中,以便在表视图控制器中访问它。当没有要显示的tv_image时,我已更新代码以将exclusionPaths重置为空数组。 - rob mayoff

0
我想知道你在哪里分配你的自定义单元格。如果没有创建单元格,难道不应该只创建一个吗?像这样:
static NSString *CellIdentifier = @"homeCell";
HomeCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(!cell)
{
    cell = [[[HomeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]];
}

然后更新UI组件。

    if([user_post.post_image isEqualToString:@"none"] && [user_post.post_video isEqualToString:@"none"])
    {

        NSLog(@"NOT INSIDE THE CONDITION");

    }
    else
    {
        NSLog(@"INSIDE BEIZER PATH CONDITION");
        UIBezierPath * imgRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, 10, 100, 100)];
        cell.tv_post.textContainer.exclusionPaths = @[imgRect];
        UIImageView *tv_image =[[UIImageView alloc]initWithFrame:CGRectMake(0, 10, 100, 100)];

        if(![user_post.post_image isEqualToString:@"none"])
        {
            [tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_image]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

        }
        if(![user_post.post_video isEqualToString:@"none"])
        {
            [tv_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.post_video_thumbnail]]placeholderImage:[UIImage imageNamed:@"post_placeholder.png"]];

        }

        [cell.tv_post addSubview:tv_image];
    }

也许你应该在“HomeCell”类中编写一个“更新方法”,以便更新UI组件。

0
问题有两个方面:
  1. 单元格被重复使用,这是错失的常见情况,但不适用于您的情况。
  2. 您的代码行 [cell.img_profile setImageWithURL:...];异步的,这意味着当图像下载完成时,您的单元格可能已经被重复使用,因此在重复使用时清除imageView是无效的。

要正确地在可重用单元格中使用异步加载图像,您必须在成功加载时仔细检查它是正确的图像。

向单元格类添加一个属性:

@property (weak) NSURL *targetImageUrl;

然后更改加载以添加检查:

NSURL *targetImageUrl = [NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_post.user_profileImage]];

cell.targetImageUrl = targetImageUrl; // <!> replace latest target
[cell.img_profile
    setImageWithURLRequest: [NSMutableURLRequest requestWithURL:targetImageUrl]
    placeholderImage: [UIImage imageNamed:@"post_placeholder.png"]
    success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
        if (cell.targetImageUrl != targetImageUrl) return; // <!> this is the trick

        cell.img_profile.image = image;
    }
    failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
    }];

0

由于cellForRowAtIndexPath在单元格可见时被调用,因此我总是使用以下策略来显示需要从网络加载的图像:我要么使用objc_setAssociatedObject将图像(下载后)与包含表行数据的对象关联起来,要么创建一些哈希映射,在其中为键放置图像的某些ID。然后在cellForRowAtIndexPath中,我检查关联对象是否为空(或者哈希映射中是否存在该键的图像)。如果为空或不存在(这意味着它仍在下载),则为imageView的图像设置占位符图像,否则设置已下载的图像。下载完成后重新加载表格(或仅重新加载所需的单元格)。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接