views:

191

answers:

3

Fast intro

Edit: I have 9 cells. All 9 cells contain bundle image. 7 bundle images have some kind of transparency (in file, not as a blending option in code) to load images from web underneath them. The scrolling problem affects the table view when both bundle and web images are loaded into arrays as UIImage objects.

If I disable drawing of web images scrolling is beautiful, when I disable image from bundle scrolling is pretty OK. But together you get what you don't want to have: bad user experience.

Implementation

I am using model in witch you have a custom UITableViewCell with one custom UIView set up as Cell's backgroundView.

Custom UIView contains two Cell-sized images (320x80 px). All elements are set to be Opaque and have 1.0 Alpha property, but one of images is partially 100% transparent.

I don't reuse Cells because I failed to make them loading different images. Cell's reused one-by-one up to 9 cells overall. So I have 9 reusable Cells in memory. Maybe that's the issue.

Cell initWithStyle:reuseIdentifier method part:

CGRect viewFrame = CGRectMake(0.0f, 0.0f, 320.0f, 80.0f);
customCellView = [[CustomCellView alloc] initWithFrame:viewFrame];
customCellView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self setBackgroundView:customCellView];

CustomCellView's initialization method:

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        self.opaque = YES;
        self.backgroundColor = [UIColor UICustomColor];
    }
    return self;
}

Images are being loaded to NSMutableArray as UIImage objects from PNG web-located files (40 - 80 kBytes) with UIImage's imageWithData: method (after asynchronous download).

They are being set in for visible cells in image-loading method and set from array they were saved to in UITableViewDelegate's method tableView:cellForRowAtIndexPath: and passed through UITableViewCell with custom method to UIView.

And then drawn in UIView's drawRect: overridden method:

- (void)drawRect:(CGRect)rect {
    CGRect contentRect = self.bounds; 
    if (!self.editing) {
        CGFloat boundsX = contentRect.origin.x;
        CGFloat boundsY = contentRect.origin.y;
        CGPoint point;
        point = CGPointMake(boundsX, boundsY);
        if (firstImage)  { [firstImage  drawInRect:contentRect blendMode:kCGBlendModeNormal alpha:1.0f]; }
        if (secondImage) { [secondImage drawInRect:contentRect blendMode:kCGBlendModeNormal alpha:1.0f]; }
    }
}

As you see images are being drawn with drawInRect:blendMode:alpha: method.

Problem

Well, when UITableView is being scrolled on the device you can notice (noticing is bad for user, very bad) downtime each time it loads new cell during scrolling.

Edit: Problem is more of blending problem, in code you see that the internet image is being drawn underneath the one already in bundle - not drawing the overlapping image made scrolling 70% faster.

Thoughts

Same implementation have no downtime when loading images from bundle. Should I save images to Application Sandbox and then use them from there. It sounds very bad because you should minimize read-write operations in mobile device application, because disk is slowly dying each time you read or write.

Edit: Or maybe I should store images as Core Graphics objects?

Edit: There some clues in this question. But I don't know how do do the custom drawing effectively.

A: 

First you should try to make your table cells reusable..it will make a big difference for memory usage and responsiveness..

For loading images from the web in a tableView i used this link text solution. It uses the ASIHTTPRequest framework for downloading data async and it has custom reusable cells.

You could use it as it is or you can adapt it to your needs... You could download all your images async before you show the tableView and keep them in a cache. Use the code proposed at the link above to load the images from the cache not from the web. If you do it like this you may download more data than the user needs at one moment.(If someone is on 3G and it has limited traffic he wouldn't appreciate the extra trafic...)

A better solution would be to download the images only when they are needed. You could use the code as it is and add a cache module. After the image is downloaded save it in dictionary for example and the next time it is requested load it from there...

the last approach was enough every time i needed to load images in an UITableView cell. There was no need to save images as Core Graphics objects.

Hope it helps...

Edit: another solution for loading images with reusable cells in a tableview

SorinA.
I can use google as well.I have images in my memory as stated:"Images are being loaded to NSMutableArray as UIImage objects from PNG web-located files with UIImage's imageWithData: method (after asynchronous download)."The first example you linked is one of base in my application architecture. But images there are thumbnails 64x64 px and stored same way my images are stored.Second example is pretty much the same and pretty irrelevant to the problem.
ermik
maybe i wasn't explicit enough..try to fix the reusable cells problem. also why are you using an uiview to display the images, why not an uiimageview?it wasn't my intention to show you that i know how to use google and you don't..the point of my link was to give you an example of an uitableview that loaded images from the net using async download and reusable cells... if you already use that example maybe you should think about why their images load ok and the table scrolls ok but yours doesnt..
SorinA.
Their images load fast cause they are small. (Both size and byte-weight) I just need to draw images fast enough, and decoding UIImage in `[UIImage drawInRect:]` is the leak of speed.
ermik
A: 

Try building an image cache that is a new obj - c object that simply returns the UIImage you need, keeping up to the last 50 or so requested UIImages on hand. Then the cell goes to draw it just asks the cache object for an image - and either gets it right away, or the cache object starts to download it.

UImage* theImage = [myImageCache imageForURL:urlForPNGImage]; 
[theImage drawinRect:rectForImageInCell];

// imageCache has a dictionary of UIImages with the image stored with URL as the key. You also need to track stuff like last used time, etc so you can keep memory under control.

Also, with whatever image cache you use, you need to load the images for rows that have not yet been seen by the user. Just load 10 ahead (or some other number) on both sides of the shown rows. Then the image is ready to go. ie call

for (rows unseenButNearTheView)
 urlForRow = blah
 [myImageCache imageForURL:urlForRow]; 
Tom Andersen
I have 9 cells. All 9 cells contain bundle image. 7 bundle images have some kind of transparency (**in file, not as a blending option in code**) to load images from web underneath them. The scrolling problem affects the table view when both bundle and web images are loaded into arrays as ‘UIImage‘ objects. If I disable drawing of web images scrolling is beautiful, when I disable image from bundle scrolling is pretty OK. But together you get what you don't want to have: bad user experience. Good try anyway, thanks, but i dont have 50 cells, and there are no cells with images **to be** loaded.
ermik
I see - the only way to fix this is
Tom Andersen
+1  A: 

From http://iphoneincubator.com/blog/tag/uiimage

Something like this to combine two images. With only 9 rows you should have no delays anywhere.

- (UIImage *)addImage:(UIImage *)image1 toImage:(UIImage *)image2 {  
UIGraphicsBeginImageContext(image1.size);  

// Draw image1  
[image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];  

// Draw image2  
[image2 drawInRect:CGRectMake(0, 0, image2.size.width, image2.size.height)];  

UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();  

UIGraphicsEndImageContext();  

return resultingImage;  
}  
Tom Andersen
I guess thats the only way, thanks.
ermik