如何减少UITableView的工作量?

时间:2015-04-13 21:15:12

标签: ios objective-c uitableview parsing uiimageview

我正在创建一个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;
}

1 个答案:

答案 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;