How to Asynchronously load UITableViewcell images

2019-08-04 05:16发布

I've tried using ASyncImageView for this purpose, but I'm a bit confused as to how I'd implement it for my specific case. I currently have a MatchCenterViewController that contains a table inside of it. It's loading the images for the cells synchronously, which is causing a lot of lag when scrolling through the table. How can I modify the way I'm loading the remote images so that it's done asynchronously? My code is below:

#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#import "MatchCenterCell.h"

@interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>

@property (nonatomic, strong) UITableView *matchCenter;
@property (nonatomic, assign) BOOL matchCenterDone;
@property (nonatomic, assign) BOOL hasPressedShowMoreButton;

@end



@implementation MatchCenterViewController


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    }
    return self;
}


- (void)viewDidLoad
{
    [super viewDidLoad];

    _matchCenterDone = NO;
    _hasPressedShowMoreButton = NO;

    // Set up MatchCenter table
    self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewCellStyleSubtitle];
    self.matchCenter.frame = CGRectMake(0,70,320,self.view.frame.size.height-100);
    self.edgesForExtendedLayout = UIRectEdgeAll;
    self.matchCenter.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
    _matchCenter.dataSource = self;
    _matchCenter.delegate = self;
    [self.view addSubview:self.matchCenter];

    self.expandedSection = -1;

    _matchCenterArray = [[NSArray alloc] init];

    // Refresh button
    UIImageView *refreshImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"refresh.png"]];
    refreshImageView.frame = CGRectMake(280, 30, 30, 30);
    refreshImageView.userInteractionEnabled = YES;
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(refreshPressed:)];
    [refreshImageView addGestureRecognizer:tapGesture];
    [self.view addSubview:refreshImageView];


    // Preparing for MC and indicating loading
    self.matchCenterArray = [[NSArray alloc] init];

    UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    activityIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
    [self.view addSubview: activityIndicator];

    [activityIndicator startAnimating];

    _matchCenterDone = NO;

    // Disable ability to scroll until table is MatchCenter table is done loading
    self.matchCenter.scrollEnabled = NO;

    [PFCloud callFunctionInBackground:@"MatchCenter3"
                       withParameters:@{}
                                block:^(NSArray *result, NSError *error) {

                                    if (!error) {
                                        _matchCenterArray = result;

                                        [activityIndicator stopAnimating];

                                        [_matchCenter reloadData];

                                        _matchCenterDone = YES;
                                        self.matchCenter.scrollEnabled = YES;
                                        NSLog(@"Result: '%@'", result);
                                    }
                                }];

}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return _matchCenterArray.count;
}

//the part where i setup sections and the deleting of said sections

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 21.0f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    return 40;
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
//code snipped out for conciseness
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//Header code snipped out for conciseness
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSDictionary *currentSectionDictionary = _matchCenterArray[section];
    NSArray *top3ArrayForSection = currentSectionDictionary[@"Top 3"];

    return (top3ArrayForSection.count-1 < 1) ? 1 : top3ArrayForSection.count-1;
}

// Cell layout
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Initialize cell
    static NSString *CellIdentifier = @"MatchCenterCell";
    MatchCenterCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        // if no cell could be dequeued create a new one
        cell = [[MatchCenterCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    //[cell.contentView addSubview:cell.priceLabel];
    [cell.contentView addSubview:cell.conditionLabel];

    // No cell seperators = clean design
    tableView.separatorColor = [UIColor clearColor];

    NSDictionary *currentSectionDictionary = _matchCenterArray[indexPath.section];
    NSArray *top3ArrayForSection = currentSectionDictionary[@"Top 3"];

    if (top3ArrayForSection.count-1 < 1) {

        // title of the item
        cell.textLabel.text = @"No items found, but we'll keep a lookout for you!";
        cell.textLabel.font = [UIFont systemFontOfSize:12];

    }

    else {

        // title of the item
        cell.textLabel.text = _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row+1][@"Title"];
        cell.textLabel.font = [UIFont systemFontOfSize:14];

        // price + condition of the item
        NSString *price = [NSString stringWithFormat:@"$%@", _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row+1][@"Price"]];
        NSString *condition = [NSString stringWithFormat:@"%@", _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row+1][@"Item Condition"]];

        cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ - %@", price, condition];
        cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];

        // image of the item
        NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_matchCenterArray[indexPath.section][@"Top 3"][indexPath.row+1][@"Image URL"]]];
        [[cell imageView] setImage:[UIImage imageWithData:imageData]];

        cell.imageView.layer.masksToBounds = YES;
        cell.imageView.layer.cornerRadius = 2.5;

    }

    return cell;
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == self.expandedSection || indexPath.row <= 3) {
        return 65;
    }
    return 0;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (_matchCenterDone == YES) {
        self.itemURL = _matchCenterArray[indexPath.section][@"Top 3"][indexPath.row+1][@"Item URL"];
        [self performSegueWithIdentifier:@"WebViewSegue" sender:self];
    }
} 

@end

@implementation MoreButton
@end

3条回答
Bombasti
2楼-- · 2019-08-04 06:05
// Use background thread to avoid the laggy tableView
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    // Download or get images here
    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"url"]];
    UIImage *cellImage = [[UIImage alloc] initWithData:imageData];

    // Use main thread to update the view. View changes are always handled through main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        // Refresh image view here
        [cell.imageView setImage:cellImage];
        [cell.imageView.layer setMasksToBounds:YES];
        [cell.imageView.layer setCornerRadius:2.5f];
        [cell setNeedsLayout];
    });
});
查看更多
迷人小祖宗
3楼-- · 2019-08-04 06:12

The most common solution to this is AFNetworking's AFImageView. It handles this situation perfectly. It should take you no time at all to implement, so give it a go.

查看更多
何必那么认真
4楼-- · 2019-08-04 06:14

Guy Kogus' answer works great. He's right, I got into all kinds of issues like he mentions in the comment above, doing similar things like the first answer.

Still, here's an example on how to use AFNetworking's UIImageView category. Assuming the code below is in a Cell (or something inheriting from a UIView).

First import the class:

#import "UIImageView+AFNetworking.h"

Then add this code in your UITableViewCell:

NSString *url = @"http://www.domain.www/some_image.jpg";

[self.productImage setImageWithURL:[NSURL URLWithString:url]
                  placeholderImage:[UIImage imageNamed:@"placeholderImg.png"]];

[self setNeedsLayout];

Not 100% sure if setNeedsLayout is necessary in this case. Feel free to correct this.

查看更多
登录 后发表回答