This somewhat related to another post - but that one got kinda messed up - so I start from scratch:
I have a CATiledLayer with a HUGE image in it (5780x6700px).
The tiled layer is in a scrollview. When I start to scroll around, everything's fine up to a point where obviously too many tiles have been rendered. Then I get a memory warning and the app crashes.
I tried to remove the tiledlayer from the view when I get the memory warning, clear it's contents (rootLayer.contents = nil AND tiledLayer.contents = nil) and then re-attach it empty to the view. didn't work...
Would be thankful for any input...
Edit:
Thanks for the hints so far. Some of my assumptions seem to be wrong. I loaded the image in a function and then retained it - which is a contradiction to the CATiledLayer behaviour, I guess. Plus the UIImage imageNamed:
seemed to cause some errors as well.
Now I load the image in drawLayer:(CALayer *) inContentext(CGContextRef) ctx
and use UIImage imageWithContentsOfFile:
to load it.
This seems to work without c开发者_开发知识库ausing the memory to go low.
However - the tiles are drawn painfully slow :(
Here's my setup for the tiledLayer:
-(CATiledLayer *)initTiledLayers: (int)level{
//[tiledLayer release];
CGRect pageRect;
CATiledLayer *myLayer = [CATiledLayer layer];
myLayer.delegate = self;
if(level == 1){
pageRect = CGRectMake(0, 0, 690, 800);
myLayer.tileSize = CGSizeMake(800, 800);
myLayer.levelsOfDetail = 10;
myLayer.levelsOfDetailBias = 2;
}else if(level == 2){
pageRect = CGRectMake(0, 0, 1445, 1675);
myLayer.tileSize = CGSizeMake(600, 600);
myLayer.levelsOfDetail = 8;
myLayer.levelsOfDetailBias = 2;
}else if(level == 3){
pageRect = CGRectMake(0, 0, 2890, 3350);
myLayer.tileSize = CGSizeMake(400, 400);
myLayer.levelsOfDetail = 4;
myLayer.levelsOfDetailBias = 1;
}else if(level == 4){
pageRect = CGRectMake(0, 0, 5780, 6700);
myLayer.tileSize = CGSizeMake(150, 150);
myLayer.levelsOfDetail = 4;
myLayer.levelsOfDetailBias = 0;
}
myLayer.frame = pageRect;
return myLayer;
[myLayer release];
}
(int)level indicates which image to be used (I'm switching images on certain zoom-levels). Level4 is the biggest image...
And here's the drawLayer-function:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0);
CGContextFillRect(ctx, CGContextGetClipBoundingBox(ctx));
CGContextTranslateCTM(ctx, 0.0, layer.bounds.size.height);
CGContextClearRect(ctx, CGRectMake(0,0,layer.bounds.size.width, layer.bounds.size.height));
CGContextScaleCTM(ctx, 1.0, -1.0);
UIImage *map = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Karte0%d", actLevel] ofType:@"jpg"]];
UIImage *wanderwege = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Karte0%d_wanderwege", actLevel] ofType:@"png"]];
UIImage *rhb = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Karte0%d_rhb", actLevel] ofType:@"png"]];
UIImage *abstecher = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Karte0%d_abstecher", actLevel] ofType:@"png"]];
CGRect imageRect = CGRectMake (0.0, 0.0, map.size.width, map.size.height);
CGContextDrawImage (ctx, imageRect, [map CGImage]);
imageRect = CGRectMake (0.0, 0.0, wanderwege.size.width, wanderwege.size.height);
CGContextDrawImage (ctx, imageRect, [wanderwege CGImage]);
imageRect = CGRectMake (0.0, 0.0, rhb.size.width, rhb.size.height);
CGContextDrawImage (ctx, imageRect, [rhb CGImage]);
imageRect = CGRectMake (0.0, 0.0, abstecher.size.width, abstecher.size.height);
CGContextDrawImage (ctx, imageRect, [abstecher CGImage]);
}
Yeah - I know - I actually draw 4 images into the context. The PNGs are overlays which I need to dynamically add to the main-image...
I tried to merge the images into one - but this seems to be even more mem/cpu-consuming ;)
Any further help is greatly appreciated!
Why do you draw the all image in the delegate callback?
The problem is CATiledLayer breaks all the content into rectangles and passes clipped context into delegate callback: - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
This happens for all the rectangles and as there are many of them and the image you draw is big and time consuming, the overall process lasts "ages".
In my practice I had many small objects that were rendered only if they were visible within the frame defined by the clipped context.
In your case (1 big pdf) I would try to draw only the part of the image. Not sure if it helps anyway :)
You need to split the large image because iOS has to keep the image in memory uncompressed. If you multiply your image size by 4 bytes per pixel that's an enormous amount of ram consumed.
You need to somehow pre-split them into halves and quarters and cache these to disk, then display the tiles from there. See the WWDC 2010 scrollview talk that shows how to do that smoothly.
I found out, that I didn't use the tiledLayer correctly. Also, I was using PNGs as overlays and had to set the blend-mode on the tiledLayer correctly to get rid of the black screens...
[edit] and not to forget (as Andrew mentions below), the big image has to be cut into tiles (zoomify for PSD, for example). If you use the tiled layer with one big image, it doesn't work...
精彩评论