Изображения, загруженные асинхронно, появляются только в UITableView после нажатия или прокрутки
Я успешно загружаю миниатюры из сообщений блога асинхронно в мой UITableView.
проблема в том, что изображения появляются только при нажатии на ячейку или прокрутке вниз.
когда я нажимаю на ячейку, изображение появляется слева, толкая заголовок и подзаголовок вправо.
когда я прокручиваю вниз, изображения появляются там, где они должны быть в ячейках по мере их выявления.
вот мой код (я использую AFNetworking):
#import "UIImageView+AFNetworking.h"
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return posts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *post = [posts objectAtIndex:indexPath.row];
NSString *postpictureUrl = [post objectForKey:@"picture"];
[cell.imageView setImageWithURL:[NSURL URLWithString:postpictureUrl]];
cell.textLabel.text = [post objectForKey:@"post_text"];
cell.detailTextLabel.text = [post objectForKey:@"post_author_name"];
return cell;
}
Я вижу это в симуляторе iPhone 6.0, XCode 4.5, OSX MtLion.
есть идеи, почему изображения не рисуются на начальном экране?
5 ответов
вещь, которую вы хотите знать при смешивании asynch и таблиц, заключается в том, что asynch заканчивается в неизвестное время в будущем, возможно, после того, как ячейка прокручивается, удаляется, повторно используется и т. д.
кроме того, изображение, которое извлекается из интернета, теряется, если эта ячейка прокручивается. Не уверен, что AFNetworking кэширует для вас, но, возможно, лучше не предполагать. Вот решение, использующее собственную сеть:
// ...
NSDictionary *post = [posts objectAtIndex:indexPath.row];
NSString *postpictureUrl = [post objectForKey:@"picture"];
// find a place in your model, or add one, to cache an actual downloaded image
UIImage *postImage = [post objectForKey:@"picture_image"];
if (postImage) {
cell.imageView.image = postImage; // this is the best scenario: cached image
} else {
// notice how we don't pass the cell - we don't trust its value past this turn of the run loop
[self asynchLoad:postpictureUrl forIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:@"default"];
}
// ...
теперь, не-нонсенс asynch нагрузки без каких-либо 3-е лица
- (void)asynchLoad:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath {
NSURL *url = [NSURL urlWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error) {
// create the image
UIImage *image = [UIImage imageWithData:data];
// cache the image
NSDictionary *post = [posts objectAtIndex:indexPath.row];
[post setObject:image forKey:@"picture_image"];
// important part - we make no assumption about the state of the table at this point
// find out if our original index path is visible, then update it, taking
// advantage of the cached image (and a bonus option row animation)
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
if ([visiblePaths containsObject:indexPath]) {
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationFade];
// because we cached the image, cellForRow... will see it and run fast
}
}
}];
}
для этого сообщения должны быть созданы NSMutableDictionary...
// someplace in your code you add a post to the posts array. do this instead.
NSDictionary *postData = // however you get a new post
[posts addObject:[NSMutableDictionary dictionaryWithDictionary:postData]];
кроме того, если трудно изменить модель сообщений напрямую, вы можете настроить другую структуру для кэширования загруженных изображений. Изменяемый словарь, набираемый строками url, является хорошей структурой для использования:
@property (nonatomic,strong) NSMutableDictionary *imageCache;
@synthesize imageCache=_imageCache;
// lazy init on the getter...
- (NSMutableDictionary *)imageCache {
if (!_imageCache) {
_imageCache = [NSMutableDictionary dictionary];
}
return _imageCache;
}
теперь, при настройке ячейки, посмотрите, есть ли кэшированное изображение, проверив кэш...
// change to the cellForRowAtIndexPath method
NSString *postpictureUrl = [post objectForKey:@"picture"];
UIImage *postImage = [self.imageCache valueForKey:postpictureUrl];
и после изображение загружается, кэшируется...
// change to the asynchLoad: method I suggested
UIImage *image = [UIImage imageWithData:data];
[self.imageCache setValue:image forKey:urlString];
проблема решена путем размещения заполнителя в этой строке
...
[cell.imageView setImageWithURL:[NSURL URLWithString:postpictureUrl] placeholderImage:[UIImage imageNamed:@"default"]];
....
заполнитель должен иметь отношение размеров, подобное миниатюре, чтобы избежать искажений.
Я долго чесал голову и, наконец, понял это.
моя ошибка заключалась в том, что я устанавливал изображение в cell.imageView
когда я должен устанавливать свою фактическую розетку cell.eventImageView
. Он возился с общим imageview, предоставленным в UITableViewCell
. Надеюсь, это кому-то поможет.
Это мое решение, используя категорию для UIImageView.
Примечание: поскольку мы выполняем себя.image = nil в первой строке необходимо задать изображение-заполнитель для ячейки.Книги после вызова этого метода.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
[cell.imageView loadImageForURLString:row.imageUrl];
cell.imageView.image = tempImage;
...
}
категории:
#import "UIImageView+AsyncLoad.h"
@implementation UIImageView (AsyncLoad)
- (void)loadImageForURLString:(NSString *)imageUrl
{
self.image = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response, NSData * data, NSError * error)
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (data && self.window) {
self.image = [UIImage imageWithData:data];
}
}];
}
@end
Я очень опоздала на вечеринку, но если вы копать немного глубже в UIImageView+AFNetworking docs вы найдете способ – setImageWithURLRequest:placeholderImage:success:failure:
который вы можете использовать для перезагрузки ячейки, когда изображение доступно:
NSURLRequest *urlRequest = [NSURLRequest requestWithURL: [NSURL URLWithString: imageURL]];
__weak UITableViewCell *weakCell = cell;
[cell.imageView setImageWithURLRequest: urlRequest
placeholderImage: nil
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
__strong UITableViewCell *strongCell = weakCell;
strongCell.imageView.image = image;
[tableView reloadRowsAtIndexPaths: @[indexPath]
withRowAnimation: UITableViewRowAnimationNone];
} failure: NULL];