【问题标题】:SDWebImage displaying wrong images in UITableViewSDWebImage 在 UITableView 中显示错误的图像
【发布时间】:2015-01-13 13:47:18
【问题描述】:

在我的 iOS 应用程序中,我在多个 UITableViewCells 中显示图像。但是,它不会在每个单元格中显示正确的图像。

首先,我使用以下方法从 Feedly 流中加载一些内容:

- (void)loadStreams {
    NSString *feedName = [NSString stringWithFormat:@"%@-id", self.category];

    NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
    NSString *accessToken = [standardUserDefaults objectForKey:@"AccessToken"];
    NSString *feedId = [standardUserDefaults objectForKey:feedName];
    NSString *feedPartial = [feedId stringByReplacingOccurrencesOfString:@"/" withString:@"%2F"];
    NSString *feedUrl = [NSString stringWithFormat:@"https://sandbox.feedly.com/v3/streams/%@/contents", feedPartial];

    NSLog(@"The Feedly url is: %@", feedUrl);

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:feedUrl]];


    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    [mutableRequest addValue:accessToken forHTTPHeaderField:@"Authorization"];

    request = [mutableRequest copy];

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    operation.responseSerializer = [AFJSONResponseSerializer serializer];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {


        NSArray *jsonArray = (NSArray *)[responseObject objectForKey:@"items"];

        self.continuation = [responseObject objectForKey:@"continuation"];

        NSMutableArray *tempStreams = [[NSMutableArray alloc] init];

        for (NSDictionary *dic in jsonArray) {

            NSLog(@"Dic contains: %@", dic);

            NSDictionary *originArray = [dic objectForKey:@"origin"];
            NSDictionary *visualArray = [dic objectForKey:@"visual"];
            NSArray *alternateArray = [dic objectForKey:@"alternate"];

            NSDictionary *alternate = [alternateArray objectAtIndex:0];

            NSString *image = [visualArray objectForKey:@"url"];
            NSString *title = [dic objectForKey:@"title"];
            NSString *author = [dic objectForKey:@"author"];
            NSString *date = [dic objectForKey:@"published"];
            NSDictionary *contentum = [dic objectForKey:@"content"];
            NSString *content = [contentum objectForKey:@"content"];
            NSString *owner = [originArray objectForKey:@"title"];
            NSString *givenid = [dic objectForKey:@"id"];
            NSString *href = [alternate objectForKey:@"href"];

            NSDate *publisher = [NSDate dateWithTimeIntervalSince1970:([date doubleValue] / 1000.0)];
            NSString *published = publisher.timeAgoSinceNow;

            NSDictionary *data = [[NSDictionary alloc] initWithObjectsAndKeys:title, @"title", image, @"imageurl", published, @"published", owner, @"owner", content, @"content", givenid, @"givenid", href, @"href", author, @"author", nil];

            Stream *stream = [[Stream alloc] initWithDictionary:data];
            [tempStreams addObject:stream];
        }


        self.streams = [[NSMutableArray alloc] initWithArray:tempStreams];
        tempStreams = nil;

        [self.tableView reloadData];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Services"
                                                            message:[error localizedDescription]
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];
        [alertView show];
    }];

    [operation start];
}

这会将数据传递给名为Stream 的对象,该对象由以下代码组成:

Stream.h

#import <Foundation/Foundation.h>

@interface Stream : NSObject

@property (strong, nonatomic)NSString *name;
@property (strong, nonatomic)NSString *thumbnail;
@property (strong, nonatomic)NSString *photo;
@property (strong, nonatomic)NSString *published;
@property (strong, nonatomic)NSString *content;
@property (strong, nonatomic)NSString *givenid;
@property (strong, nonatomic)NSString *linky;
@property (strong, nonatomic)NSString *author;

- (id)initWithName:(NSString *)aName
         thumbnail:(NSString *)aThumbnail
             photo:(NSString *)aPhoto
         published:(NSString *)aPublished
           content:(NSString *)aContent
           givenid:(NSString *)aId
             linky:(NSString *)aLinky
            author:(NSString *)aAuthor;

- (id)initWithDictionary:(NSDictionary *)dic;

@end

Stream.m

#import "Stream.h"

@implementation Stream

//The designed initializer
- (id)initWithName:(NSString *)aName
         thumbnail:(NSString *)aThumbnail
             photo:(NSString *)aPhoto
         published:(NSString *)aPublished
           content:(NSString *)aContent
           givenid:(NSString *)aId
              linky:(NSString *)aLinky
             author:(NSString *)aAuthor{
    self = [super init];

    if (self) {
        self.name = aName;
        self.thumbnail = aThumbnail;
        self.photo = aPhoto;
        self.published = aPublished;
        self.content = aContent;
        self.givenid = aId;
        self.linky = aLinky;
        self.author = aAuthor;
    }

    return self;
}

- (id)initWithDictionary:(NSDictionary *)dic {
    self = [self initWithName:dic[@"title"] thumbnail:dic[@"imageurl"] photo:dic[@"imageurl"] published:dic[@"published"] content:dic[@"content"] givenid:dic[@"givenid"] linky:dic[@"href"] author:dic[@"author"]];
    return self;
}

- (id)init {
    self = [self initWithName:@"Undifined" thumbnail:@"Undifined" photo:@"Undifined" published:@"Undifined" content:@"Undifined" givenid:@"Undifined" linky:@"Undifined" author:@"Undifined"];
    return self;
}

@end

最后我构建了一个这样的单元:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * reuseIdentifier = @"programmaticCell";
    MGSwipeTableCell * cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    if (!cell) {
        cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
    }

    CGFloat brightness = [UIScreen mainScreen].brightness;

    cell.textLabel.text = [self.streams[indexPath.row] name];
    cell.detailTextLabel.text = [self.streams[indexPath.row] published];

    NSString *imageUrl = [NSString stringWithFormat: @"%@", [self.streams[indexPath.row] photo]];

    NSLog(@"Image is: %@ and path is: %d", imageUrl, indexPath.row);

    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:imageUrl]
                      placeholderImage:[UIImage imageNamed:@"tile-blue.png"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];

    cell.delegate = self; //optional

    return cell;
}

但是,它会在很多单元格中显示错误的图像,有时会在几个单元格中显示相同的图像。我在这里做错了什么?

【问题讨论】:

  • NSLog 中的 imageUrl 打印的路径总是正确的?
  • 是的,它确实打印出正确的路径。
  • 在我的表格视图中我使用:[cell.userImage sd_setImageWithPreviousCachedImageWithURL:URL andPlaceholderImage:placeholder options:2 progress:nil completed:nil];并为我提供更多不同的图像
  • 是的,因为您之前缓存了图像。我不是。我正在下载,因为表格正在构建。
  • 尝试删除自我。对于self.tableview

标签: ios objective-c uitableview sdwebimage


【解决方案1】:

这些是单元重用的症状。您必须处理两个问题。

(1) 您应该在重新使用之前重置单元格的内容。为此,您可以覆盖单元格中的prepareForReuse 并清除相关属性(例如cell.imageView)。如果您不这样做,您将在单元格被回收之后,SDWebImage 分配新图像之前看到旧图像。

(2) 由于SDWebImage 图像检索是异步的,图像可能在单元格滚出屏幕后到达(并与新内容一起回收。您需要在将图像分配给 imageView 之前检查图像是否仍然相关. 我不确定SDWebImage UIImageView 类别方法是否可行。您可能需要稍微剖析 SDWebImage 。您可以使用SDWebImageManager 方法更好地控制该过程:

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

你可以像这样使用它(CellForRowAtIndexPath

[[SDWebImageManager defaultManager] downloadImageWithURL:url 
                                                options:0 
                                               progress:nil 
                                              completed:
^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
     if ([[tableView indexPathForCell:cell] isEqual:indexPath]) {
           //image is still valid for this cell
           cell.image = image;
      }
}];

【讨论】:

  • 感谢您的明确解释。不过,我确实收到您的示例代码错误。 Incompatible block pointer types sending '^(UIImage *image, SDImageCacheType cacheType, NSError *error, BOOL finished)' to parameter
  • @user4334509 - 抱歉,我想我为SDWebImageCompletionWithFinishedBlock 使用了错误的块签名。现已更正。我认为;-j
  • 天哪,这太烦人了。它仍在加载错误的图像:S
【解决方案2】:
  1. 在您关闭之前在堆栈上推送一个唯一 ID,并在您的关闭完成时检查它
  2. prepareForReuse

像这样:

func updateArtistImage(url: URL) {
        let _eventId = self.event?.id
        SDWebImageManager.shared().loadImage(with: url, options: [], progress: nil) { (image, data, error, cacheType, finished, url) in
            if self.event!.id == _eventId {
                if error == nil {
                    self.artistImageView.image = image
                } else {
                    self.artistImageView.image = UIImage(named: "error_image")
                }
            }
        }
    }

还有这个:

override func prepareForReuse() {
    super.prepareForReuse()
    self.artistImageView.image = nil
}

【讨论】:

    猜你喜欢
    • 2015-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多