SDWebImage отображает неправильные изображения в UITableView

В моем приложении для 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;
}

Однако происходит то, что он отображает неправильное изображение во многих ячейках, а иногда одно и то же изображение для пары ячеек. Что я здесь делаю неправильно?


person user4334509    schedule 13.01.2015    source источник
comment
imageUrl в NSLog всегда печатает правильный путь?   -  person weso    schedule 13.01.2015
comment
Да, он распечатывает правильный путь.   -  person user4334509    schedule 13.01.2015
comment
В своем табличном представлении я использую: [cell.userImage sd_setImageWithPreviousCachedImageWithURL: URL andPlaceholderImage: параметры заполнителя: 2 прогресс: ноль завершен: ноль]; и работай для меня с большим количеством разных изображений   -  person weso    schedule 13.01.2015
comment
Да, потому что вы ранее кэшировали изображения. Я не. Скачиваю по мере сборки таблицы.   -  person user4334509    schedule 13.01.2015
comment
Попробуйте удалить себя. Для self.tableview   -  person Tom    schedule 13.01.2015
comment
В методе делегата табличного представления *   -  person Tom    schedule 13.01.2015


Ответы (2)


Это симптомы повторного использования клеток. Есть две проблемы, с которыми вам придется иметь дело.

(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;
      }
}];
person foundry    schedule 13.01.2015
comment
Спасибо за ясное объяснение. Однако я получаю сообщение об ошибке с вашим примером кода. Incompatible block pointer types sending '^(UIImage *image, SDImageCacheType cacheType, NSError *error, BOOL finished)' to parameter - person user4334509; 13.01.2015
comment
@user4334509 user4334509 - извините, кажется, я использовал неправильную подпись блока для SDWebImageCompletionWithFinishedBlock. Исправлено сейчас. я думаю ;-j - person foundry; 13.01.2015
comment
Боже, это так раздражает. Он все еще загружает неправильные изображения: S - person user4334509; 13.01.2015

  1. Поместите уникальный идентификатор в стек перед закрытием и проверьте его, когда закрытие завершится.
  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
}
person Tony    schedule 18.07.2017