我正在创建一个UITableViewController来显示曲棍球队的名单。 tableViewController调用web来获取播放器的统计信息以及在tableViewCell中显示的小图片。但是,当我滚动TableView时,它并不平滑。它令人难以置信的参差不齐。我怎样才能这样做(如果这会减少它的工作量),播放器的图片不会加载,直到它们出现在屏幕上?这是我当前的代码(我已经将UITableViewCell子类化了):
编辑:我已编辑了我的代码,以便按照以下评论进行操作。属性imagesCache实际上是一个UIMutableDictionary(令人困惑,抱歉)。但是,现在我收到了错误:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: object cannot be nil (key: http://app-assets3.sportngin.com/app_images/noPhoto-square.jpg?1428933774)'
*** First throw call stack:
(0x1865f6530 0x1975cc0e4 0x1864e1348 0x1000496a8 0x185f87168 0x1874d3be8 0x187425374 0x187414ecc 0x1874d694c 0x1000acf94 0x1000b7db8 0x1000b02c4 0x1000ba5d4 0x1000bc248 0x197dfd22c 0x197dfcef0)
libc++abi.dylib: terminating with uncaught exception of type NSException
这是我的代码:
#import "RosterTableTableViewController.h"
#import "TFHpple.h"
#import "RosterListing.h"
#import "RosterListingCellTableViewCell.h"
@interface RosterTableTableViewController ()
@property (nonatomic, strong) NSMutableArray *rosters;
@property (nonatomic, strong) NSMutableDictionary *imagesDictionary;
@property NSMutableDictionary *imageCache;
@end
@implementation RosterTableTableViewController
- (void) loadRoster
{
NSURL *RosterURL = [NSURL URLWithString:@"http://www.lancers.com/roster/show/1502650?subseason=197271"];
NSData *RosterHTMLData = [NSData dataWithContentsOfURL:RosterURL];
TFHpple *RosterParser = [TFHpple hppleWithHTMLData:RosterHTMLData];
// Get the data
NSString *RosterNumberPathQueryString = @"//tbody[@id='rosterListingTableBodyPlayer']/tr/td[@class='number']";
NSArray *RosterNumberNodes = [RosterParser searchWithXPathQuery:RosterNumberPathQueryString];
NSString *RosterNamePathQueryString = @"//tbody[@id='rosterListingTableBodyPlayer']/tr/td[@class='name']/a";
NSArray *RosterNameNodes = [RosterParser searchWithXPathQuery:RosterNamePathQueryString];
NSString *RosterImagePathQueryString = @"//tbody[@id='rosterListingTableBodyPlayer']/tr/td[@class='photo']/a/img";
NSArray *RosterImageNodes = [RosterParser searchWithXPathQuery:RosterImagePathQueryString];
NSMutableArray *rosterItems = [[NSMutableArray alloc] initWithCapacity:0];
for (int i = 0; i < RosterNumberNodes.count; ++i) {
RosterListing *thisRosterListing = [[RosterListing alloc] init];
thisRosterListing.playerNumber = [[[RosterNumberNodes objectAtIndex:i] firstChild] content];
thisRosterListing.playerName = [[[RosterNameNodes objectAtIndex:i] firstChild] content];
thisRosterListing.playerURL = [[RosterNameNodes objectAtIndex:i] objectForKey:@"href"];
@try {
thisRosterListing.playerImageURL = [[RosterImageNodes objectAtIndex:i] objectForKey:@"src"];
}
@catch (NSException *e) {}
/*
NSLog(@"%@", thisRosterListing.playerNumber);
NSLog(@"%@", thisRosterListing.playerName);
NSLog(@"%@", thisRosterListing.playerURL);
NSLog(@"%@", thisRosterListing.playerImageURL);
*/
[rosterItems addObject:thisRosterListing];
}
self.rosters = rosterItems;
}
- (instancetype) initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
self.navigationItem.title = @"Roster";
self.imageCache = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self loadRoster];
// Load the Cell NIB file
UINib *nib = [UINib nibWithNibName:@"RosterListingCellTableViewCell" bundle:nil];
// Register this NIB, which contains the cell
[self.tableView registerNib:nib forCellReuseIdentifier:@"RosterCell"];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 54;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return self.rosters.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get a new or recycled cell
RosterListingCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"RosterCell" forIndexPath:indexPath];
RosterListing *thisRosterListing = [self.rosters objectAtIndex:indexPath.row];
cell.playerNumberLabel.text = thisRosterListing.playerNumber;
cell.playerNameLabel.text = thisRosterListing.playerName;
__block UIImage *image = [self.imageCache objectForKey:thisRosterListing.playerImageURL];
cell.imageView.image = image;
if(image == nil) {
//If nil it's not downloaded, so we download it,
//We MUST download in a separate thread otherwise the scroll will be really slow cause the main queue will try to download each cell as they show up and every time they show up
NSURL *imageURL = [NSURL URLWithString: thisRosterListing.playerImageURL];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:imageURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Completion Handler is executed in an async way
if([self.imageCache objectForKey:thisRosterListing.playerImageURL] == nil)
self.imageCache[thisRosterListing.playerImageURL] = image;
//We need to execute the image update in the main queue otherwise it won't work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
RosterListingCellTableViewCell *aCell = (RosterListingCellTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
aCell.imageView.image = image;
}];
}];
[dataTask resume];
}
return cell;
}
答案 0 :(得分:0)
在UITableViewCells中处理图像起初可能有点棘手,我确实有一个可以帮助你的代码,在我搜索它时给我一点时间。
基本上您要做的是检查您下载图像的行是否仍然显示(因为用户可以比下载图像更快地滚动),并且在下载结束后将其存储在本地,这样您就不必下载再一次。
编辑:这是代码,对不起迟到
@propery NSMutableDictionary *imagesDictionary;
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
//...
//We load the model information for that cell
NSDictionary *cellInfo = self.dataModel[indexPath.row];
__block UIImage *image = [self.imagesDictionary objectForKey:[cellInfo objectForKey:@"avatar"]];
cell.avatarView.image = image;
if(image == nil) {
//If nil it's not downloaded, so we download it,
//We MUST download in a separate thread otherwise the scroll will be really slow cause the main queue will try to download each cell as they show up and every time they show up
NSURL *imageURL = [NSURL URLWithString:URL];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:imageURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Completion Handler is executed in an async way
if([self.imagesDictionary objectForKey:[cellInfo objectForKey:@"avatar"]] == nil)
if(error == nil) {
// no error
image = [UIImage imageWithData:data];
if (image == nil) {
//nil image, in my case I use a default undefined image
image = [UIImage imageNamed:@"undefined_user"];
}
//Now we are sure image is never nil
[self.imagesDictionary setObject:image forKey:[cellInfo objectForKey:@"avatar"]];
//We need to execute the image update in the main queue otherwise it won't work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
UITableviewCell *aCell = (UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
aCell.avatarView.image = image;
}];
}];
[dataTask resume];
return cell;