views:

2263

answers:

4

Hi Guys...

Check out the last lines before [return cell]..After the images are being loaded the scrolling speed is decreasing..it seems the scroll gets stuck

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *MyIdentifier = @"MyIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
    }

    int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
    NSString *itemDescription=[[stories objectAtIndex: storyIndex]
                                                 objectForKey:@"title"];

    CGRect aframe = CGRectMake(80, 30, 250, 40); 
    textLabel = [[UILabel alloc] initWithFrame:aframe];
        textLabel.font = [UIFont boldSystemFontOfSize:14];
    textLabel.numberOfLines=0;
    textLabel.textColor = [UIColor darkTextColor];
    textLabel.backgroundColor = [UIColor whiteColor];
    [cell.contentView addSubview:textLabel];
    textLabel.text=itemDescription;

    CGRect frame = CGRectMake(0, 0, 70,80);
    UIImageView *TopImageView = [[UIImageView alloc] init];
    [cell.contentView addSubview:TopImageView];
    TopImageView.frame=frame;

    m_strImage = [m_imglinkArray objectAtIndex:storyIndex]; 

    TopImage = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:m_strImage]]];
    TopImageView.image=TopImage;

    return cell;
}

Could you guys help me to increase the speed of the scroll?

Regards

Arun

A: 

It seems that you are loading from a url every time the table view asks to get the cell for a row. This is happening in the main application thread (the User interface thread) and it blocks the user interface, making it not responsive.

Especially if this url is a resource that you load from the internet then this pause is big.

I would recommend that you should load the images in a background thread and display a placeholder image until they are fetched from the network.

You can try the solution provided in the following url...

http://www.markj.net/iphone-asynchronous-table-image/

Panagiotis Korros
+2  A: 

I believe you may have two problems here. First is the issue of not multi-threading the image loads and second is how you're using UITableViewCell.

iPhone calls cellForRowAtIndexPath whenever a cell appears on the screen - if you scroll down, everything above is unloaded and reloaded when you scroll back up again. As already noted this can be solved with multithreading patterns - either the link posted to markj.net, and/or by using NSThread. The advantage of NSThread is you have more flexibility to handle problems or load more than just image data. (I would use both)

In order to do this efficiently you need to have a custom UITableViewCell. This is good to have regardless because you can make the table cell responsible for it's content rather than your view controller, and you can efficiently manipulate cell formatting and content. Here is a link to a great post about UITableCellView: http://blog.atrexis.com/index.cfm/2009/1/6/iPhone--Customize-the-UITableCellView

When you implement UITableViewCell, do your best to put all addSubView and formatting calls in the "if(cell==nil)" block, only if you can't put them in your custom initWithFrame. You'll find the iPhone is stacking every subview you add -- it's not clearing the old view. You can see this at work by setting all the background colors to "UIColor clearColor" and then change text between hits -- you'll see a stack of text on top of each other. This isn't normally visible because solid-filled backgrounds draw over all the "old" subviews.

Before you combine these two methods, considering implementing a "model". You've got a view and a controller but all of your data should be in a model. All of the image URLs, content, etc. should be in something like an NSMutableArray, a custom object of your own, or maybe via SQLite. Once you have a model, you can implement your caching of the images. With caching, all of the images will be retained between loads of your application which will save batteries, bandwidth, and time.

I would: 1. put all your data in a model of some kind (NSMutableArray, SQLLite, something) 2. Implement your own custom UITableViewCell 3. Make sure you have 0 addSubView, init, or alloc calls inside the active block of cellForRowAtIndexPath 4. Use delegation example or NSThread to load all images in the background 5. Add local image caching

There are some good examples out in about and on the Apple forums on caching images.

nessence
+2  A: 

nessence has a lot of good information. A few more thoughts:

You're leaking like crazy here. Every cell you create, you leak 2 UILabels, a UIImageView and a UIImage.

As noted before, not only are you leaking these, they're accumulating in your view because you're sticking one on top of the other with addSubview:.

Reaching out to the network during a cell draw is incredibly slow and is blocking your UI. If these URLs are local, then you can use UIImage's +imageWithContentsOfFile, if not, you need to load this in the background.

I don't think you need a thread here. NSURLConnection is an excellent way to load data in the background without incurring the overhead of threading.

nessence is completely correct that you need a model class for Story.

Your basic approach to reusable cell configuration is incorrect. You don't fetch a reusable cell and then add subviews to it. All your subviews should be added in the if() block to create the cell. Then, in each pass, you just change the values of things. I've rewritten some of your code below to demonstrate. This is still not correct code, because it is reaching out to the network during a cell draw, and this may be too many elements to be in a subview-cell (rather than a custom cell), but it's closer to the right idea. I don't even know if this compiles; I just typed it here.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *MyIdentifier = @"MyIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

    if (cell == nil) {
        // Here we do all our cell creation
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];

        // Make the label
        CGRect aframe = CGRectMake(80, 30, 250, 40);
        UILabel *textLabel = [[[UILabel alloc] initWithFrame:aframe] autorelease]; // Note the -autorelease so we don't leak

        textLabel.font = [UIFont boldSystemFontOfSize:14];
        textLabel.numberOfLines=0;
        textLabel.textColor = [UIColor darkTextColor];
        textLabel.backgroundColor = [UIColor whiteColor];
        textLabel.tag = DescriptionTag; // A tag so we can find it later (you'll need a constant for this)
        [cell.contentView addSubview:textLabel];

        // And the second label
        aframe = CGRectMake(80, 30, 250, 40);
        textLabel = [[[UILabel alloc] initWithFrame:aframe] autorelease];
        textLabel.font = [UIFont boldSystemFontOfSize:14];
        textLabel.numberOfLines=0;
        textLabel.textColor = [UIColor darkTextColor];
        textLabel.backgroundColor = [UIColor whiteColor];
        textLabel.tag = TitleTag;
        [cell.contentView addSubview:textLabel];

        // The image view
        CGRect frame = CGRectMake(0, 0, 70,80);
        UIImageView *topImageView = [[[UIImageView alloc] init] autorelease];
        topImageView.frame = frame;
        topImageView.tag = TopImageTag;
        [cell.contentView addSubview:topImageView];
    }

    // all the above was cell creation; we do that as seldom as possible.
    // Now we do cell configuration. We want this to be fast.

    UILabel *descriptionLabel = (UILabel*)[cell.contentView viewWithTag:DescriptionTag];
    descriptionLabel.text = itemDescription;

    UILabel *titleLabel = (UILabel*)[cell.contentView viewWithTag:TitleTag];
    titleLabel.text =[[stories objectAtIndex:indexPath.row] objectForKey:@"title"];

    NSString *imageURLString = [m_imglinkArray objectAtIndex:storyIndex]; // You should have a model class called Story, not two arrays.
    UIImage *image = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURLString]]] autorelease]; // This is still way too slow if it's a remote URL 
    UIImageView *imageView = (UIImageView*)[cell.contentView viewWithTag:TopImageTag];
    imageView.image = image;    

    return cell;
}

I recommend you spend some quality time studying TableViewSuite, Practical Memory Management, and Coding Guidelines for Cocoa. Some time studying the basics of Cocoa would be useful as well, because the coding style here indicates that you may not have a solid foundation. Though it's a Mac book, I still recommend Cocoa Programming for Mac OS X. If you're interested in using this to learn iPhone, I've put together a syllabus that might help. I haven't reviewed it yet, but Stanford's online CS193P course looks promising.

Rob Napier
+1  A: 

this has been asked several times here, what you need to do is to load asynchronously the images for each cell in order to prevent the scroll to slow down.

This is called "Lazy image loading" and I'm referencing the apple tutorial here : http://stackoverflow.com/questions/1130089/lazy-load-images-in-uitableview/1829035#1829035

yonel