12

ブログ投稿から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、OSXMtLionで見られます。

画像が初期画面に描画されない理由はありますか?

4

5 に答える 5

12

非同期とテーブルを混在させるときに注意したいのは、非同期が将来の未知の時間に終了することです。おそらく、セルがスクロールアウト、削除、再利用された後などです。

また、そのセルをスクロールして離すと、Webからプルされた画像が失われます。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"];
}
// ...

さて、サードパーティの助けなしにナンセンスな非同期ロード

- (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];
于 2012-09-29T20:41:16.353 に答える
5

この行にプレースホルダーを配置することで、この問題は解決されます

...
[cell.imageView setImageWithURL:[NSURL URLWithString:postpictureUrl] placeholderImage:[UIImage imageNamed:@"default"]];
....

プレースホルダーは、歪みを避けるためにサムネイルと同様の寸法比を持つ必要があります。

于 2012-09-29T20:08:46.510 に答える
2

私は長い間頭をかいて、ついにそれを理解しました。

私の間違いはcell.imageView、実際のコンセントを設定する必要があるときに画像を設定していたことでしcell.eventImageViewた。で提供されている一般的なimageviewをいじっていましたUITableViewCell。それが誰かを助けることを願っています。

于 2014-12-05T16:13:58.467 に答える
1

これは私の解決策であり、UIImageViewのカテゴリを使用しています。
注:最初の行でself.image = nilを実行するため、このメソッドを呼び出した後、cell.ImageViewのプレースホルダー画像を設定する必要があります。

- (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
于 2012-10-24T07:04:31.497 に答える
0

私はパーティーにかなり遅れていますが、UIImageView + AFNetworkingのドキュメント– 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];
于 2013-08-20T19:30:12.480 に答える