0

I have a tableview in xcode that currently downloads from the parse.com backend and displays into the table. It will download a small image and render the image into a circle as well as style each row. Currently it is downloading about 5 rows and outputs lovely but as soon as i scroll up and down a few times the tableview and app becomes slow.

Doing some research i can see suggestions to add the fetching of data on another thread. Currently i am new to all this and i was wondering if any one could assist in showing me how it's done.

My CellForRowAtIndexPath looks like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

tableView.separatorColor = [UIColor colorWithRed:192.0/255.0 green:196.0/255.0 blue:202.0/255.0 alpha:1];

if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
    [self.tableView setSeparatorInset:UIEdgeInsetsZero];
}


if (cell == nil)
{
    cell = [[UITableViewCell alloc]
            initWithStyle:UITableViewCellStyleDefault
            reuseIdentifier:CellIdentifier];
}



PFObject *games = [self.games objectAtIndex:indexPath.row];

//PFFile *imageFile = [games objectForKey:@"gameCover"];


NSString *gameName = [games objectForKey:@"name"];
NSArray *gamePlatforms = [games objectForKey:@"platform"];

NSString *platformsResult = [gamePlatforms componentsJoinedByString:@""];



NSDate *rDate = [games objectForKey:@"release_date"];



// Check to see if the date is set to a Quarter instead
if (rDate == (id)[NSNull null]) {

    self.releaseDate = @"Q1/2014";

} else {

    // Date on the right added to a subview

    //Setup date

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    //uncomment to get the time only
    //[formatter setDateFormat:@"hh:mm a"];
    [formatter setDateFormat:@"dd"];
    //[formatter setDateStyle:NSDateFormatterLongStyle];


    //get the date today


    NSDate *start = [NSDate date];

    NSDateFormatter *f = [[NSDateFormatter alloc] init];
    [f setDateFormat:@"YYYY-MM-dd"];

    NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *components = [gregorianCalendar components:NSDayCalendarUnit
                                                        fromDate:start
                                                          toDate:rDate
                                                         options:0];

    //NSLog(@"%ld", (long)[components day]);

    NSInteger value = [components day];


    NSString *myString = [NSString stringWithFormat:@"%0.0f days", (float)value];


    if ([myString  isEqual: @"1 days"])
    {
        myString = @"1 day";
    }

    self.releaseDate = myString;


}





// Cell Style
// Background Color

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10, 15, 300.0, 100.0)];
imageView.userInteractionEnabled = NO;
imageView.backgroundColor = [UIColor colorWithRed:209.0/255.0 green:230.0/255.0 blue:244.0/255.0 alpha:1];

// Icon Image



//Game Title Label
UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(80, 25, 200, 20)];
titleLabel.text = gameName;
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.font = [UIFont fontWithName:@"ProximaNova-Light" size:13];
titleLabel.textColor = [UIColor colorWithRed:44.0/255.0 green:127.0/255.0 blue:178.0/255.0 alpha:1];

// Game Date Label

UILabel *dateLabel = [[UILabel alloc]initWithFrame:CGRectMake(250, 80, 50, 20)];
dateLabel.text = self.releaseDate;
dateLabel.backgroundColor = [UIColor clearColor];
dateLabel.font = [UIFont fontWithName:@"ProximaNova-Light" size:13];
dateLabel.textColor = [UIColor colorWithRed:44.0/255.0 green:127.0/255.0 blue:178.0/255.0 alpha:1];
dateLabel.textAlignment = NSTextAlignmentRight;

// Game Platforms

UILabel *platformLabel = [[UILabel alloc]initWithFrame:CGRectMake(250, 25, 50, 20)];
platformLabel.text = platformsResult;
platformLabel.backgroundColor = [UIColor clearColor];
platformLabel.font = [UIFont fontWithName:@"ProximaNova-Light" size:13];
platformLabel.textColor = [UIColor colorWithRed:44.0/255.0 green:127.0/255.0 blue:178.0/255.0 alpha:1];
platformLabel.textAlignment = NSTextAlignmentRight;

// add the styles to the subview

[cell addSubview:imageView];
//[cell addSubview:iconView];
[cell addSubview:titleLabel];
[cell addSubview:dateLabel];
[cell addSubview:platformLabel];

PFFile *imageFile = [games objectForKey:@"gameCover"];

// If no image

if (imageFile != NULL) {
    NSURL *imageFileUrl = [[NSURL alloc] initWithString:imageFile.url];
    NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
    UIImage *myImage = [UIImage imageWithData:imageData];

    UIImageView *iconView = [[UIImageView alloc] initWithImage:myImage];
    iconView.frame = CGRectMake(20.0, 25.0, 50.0, 50.0);

    iconView.layer.masksToBounds = YES;
    iconView.layer.cornerRadius = 25.0f;

    [cell addSubview:iconView];
} else {

    UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(20.0, 25.0, 50.0, 50.0)];
    iconView.userInteractionEnabled = NO;
    iconView.image = [UIImage imageNamed:@"img.png"];

    iconView.layer.masksToBounds = YES;
    iconView.layer.cornerRadius = 25.0f;

    [cell addSubview:iconView];
}

return cell;   
}

Sorry for the mess, i can post more code if anyone would like it.

Thanks

rmaddy
  • 314,917
  • 42
  • 532
  • 579
tutchmedia
  • 139
  • 2
  • 12
  • 2
    You are synchronously downloading images on main thread which is the cause. Recommended way to deal with this is [Lazy loading images in `UITableView`](http://stackoverflow.com/questions/1130089/lazy-load-images-in-uitableview). – Amar Nov 22 '13 at 10:44
  • what kind of project have you created? ARC or not? – alinoz Nov 22 '13 at 10:45
  • 1
    Yes U need to fetch in another thread, but your title is "TableView slows down after scrolling up and down a few times" and that is due to stacking your views in your cell, so that is another problem that U have to address, along with the async download – AntonijoDev Nov 22 '13 at 11:06

5 Answers5

1

Because you are downloading images in cellForRowAtIndexPath. You must have to download images Asynchronously. Because of this performance of application will be increase. So try to download asynchronously.

iSmita
  • 1,292
  • 1
  • 9
  • 24
1

Hmmm, U R constantly adding views to UITableViewCell:

[cell addSubview:imageView];
//[cell addSubview:iconView];
[cell addSubview:titleLabel];
[cell addSubview:dateLabel];
[cell addSubview:platformLabel];

U should add them only once in if(cell == nil) block cause they R reusable cells..

Do it like this:

if (cell == nil)
{
    cell = [[UITableViewCell alloc]
            initWithStyle:UITableViewCellStyleDefault
            reuseIdentifier:CellIdentifier];


// Cell Style
// Background Color

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(10, 15, 300.0, 100.0)];

[imageView setTag:100];
imageView.userInteractionEnabled = NO;
imageView.backgroundColor = [UIColor colorWithRed:209.0/255.0 green:230.0/255.0 blue:244.0/255.0 alpha:1];

//Game Title Label
UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(80, 25, 200, 20)];
[titleLabel setTag:101];
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.font = [UIFont fontWithName:@"ProximaNova-Light" size:13];
titleLabel.textColor = [UIColor colorWithRed:44.0/255.0 green:127.0/255.0 blue:178.0/255.0 alpha:1];

// Game Date Label

UILabel *dateLabel = [[UILabel alloc]initWithFrame:CGRectMake(250, 80, 50, 20)];
[dateLabel setTag:102];
dateLabel.backgroundColor = [UIColor clearColor];
dateLabel.font = [UIFont fontWithName:@"ProximaNova-Light" size:13];
dateLabel.textColor = [UIColor colorWithRed:44.0/255.0 green:127.0/255.0 blue:178.0/255.0 alpha:1];
dateLabel.textAlignment = NSTextAlignmentRight;

// Game Platforms

UILabel *platformLabel = [[UILabel alloc]initWithFrame:CGRectMake(250, 25, 50, 20)];
[platformLabel setTag:103];
platformLabel.text = platformsResult;
platformLabel.backgroundColor = [UIColor clearColor];
platformLabel.font = [UIFont fontWithName:@"ProximaNova-Light" size:13];
platformLabel.textColor = [UIColor colorWithRed:44.0/255.0 green:127.0/255.0 blue:178.0/255.0 alpha:1];
platformLabel.textAlignment = NSTextAlignmentRight;

// add the styles to the subview

[cell addSubview:imageView];
//[cell addSubview:iconView];
[cell addSubview:titleLabel];
[cell addSubview:dateLabel];
[cell addSubview:platformLabel];

......
}

UILabel *lbl = [cell viewWithTag:101];
lbl.text = gameName;

lbl = [cell viewWithTag:102];
lbl.text = self.releaseDate;

lbl = [cell viewWithTag:103];
lbl.text = platformsResult;

Do the same thing for your ImageViews, and U will enter the world of reusability and fast tables :)

And also U shouldn't download images like that, U should use some async approach like others recomended

AntonijoDev
  • 1,317
  • 14
  • 29
  • Thanks for your help, I will start with this first and then move onto the image loading on a new thread bit. When I try to use your code I get an error on this part: UILabel *lbl = [cell viewWithTag:101]; It says: "Incompatible pointer types initializing 'UILabel *' with an expression of type 'UIView *' – tutchmedia Nov 22 '13 at 14:46
  • U mean warning? just cast like this: UILabel *lbl = (UILabel *)[cell viewWithTag:101]; – AntonijoDev Nov 22 '13 at 15:30
  • Yes, sorry i meant a warning. Changed it to what you said and ran the app, nothing shows up :( Any ideas? – tutchmedia Nov 22 '13 at 16:01
  • I didn't write the whole cod, just enought to remove your problem... Put some breakpoints in it, observe what happens and then report your findings, then will try to figure it out, in meanwhile its friday time for resting :) – AntonijoDev Nov 22 '13 at 16:08
0

Use this code to download your image asynchronously in your cellForRowAtIndexPath

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
        dispatch_async(queue, ^(void) {

            NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:parsedData[@"imageLR"]];

            UIImage* image = [[UIImage alloc] initWithData:imageData];
            if (image) {
                 dispatch_async(dispatch_get_main_queue(), ^{
                     if (cell.tag == indexPath.row) {
                         cell.imageView.image = image;
                         [cell setNeedsLayout];
                     }
                 });
             }
        });
Jay Gajjar
  • 2,661
  • 2
  • 21
  • 35
  • This won't work, since you cannot set the cell's image in the completion handler: `cell` was captured at the time as the block has been dispatched. When the completion handler will be eventually executed, the cell is "out of sync" with the corresponding index, since it has been re-used in the meantime. It also doesn't address other important problems like invoking several requests for the same URL. – CouchDeveloper Nov 22 '13 at 11:32
0

replace the code in if no image with this
// If no image

if (imageFile != NULL) {
    NSURL *imageFileUrl = [[NSURL alloc] initWithString:imageFile.url];
dispatch_queue_t callerQueue = dispatch_get_current_queue();
dispatch_queue_t downloadQueue = dispatch_queue_create("Image Downloader", NULL);
dispatch_async(downloadQueue, ^{
    NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
    dispatch_async(callerQueue, ^{
        UIImage *myImage = [UIImage imageWithData:imageData];

    UIImageView *iconView = [[UIImageView alloc] initWithImage:myImage];
    iconView.frame = CGRectMake(20.0, 25.0, 50.0, 50.0);

    iconView.layer.masksToBounds = YES;
    iconView.layer.cornerRadius = 25.0f;

    [cell addSubview:iconView];
    });
});
}
Suhit Patil
  • 11,748
  • 3
  • 50
  • 60
  • While the "direction" to solve one of the issue is correct, it introduces a few more: 1. DONT USE `dispatch_get_current_queue` (this API is deprecated for a reason!), 2. the cell will be "out of sync" with the image when the completion handler gets finally executed – CouchDeveloper Nov 22 '13 at 11:26
0

User SDWebImageCache

Image is download in background by this class and Image caching in handle by this class, And your table will scroll smooth Follow this link Here:

Jignesh Mayani
  • 6,937
  • 1
  • 20
  • 36