Mechanism to assign UIImages their own Rect inside a CALayer? - iphone

I have one or more CALayers which represent/fill the whole screen.
I want to fill those CALayers from a grid structure, using a lot of small Rects with images.
What i tried was assigning each Rect its own CALayer and an UIImage.
With over 6000 CALayers the result was very slow. Now i am looking for a way not to use that much CALayer. Maybe only the top level ones.
So ist there a mechanism to assign UIImages their own Rect inside a CALayer ?
Slow / bad code Example:
- (void)createLayer{
CALayer *currentLayer = self.layer;
_layerArray = [[NSMutableArray alloc] initWithCapacity:WIDTH*HEIGHT];
int cellSize = self.bounds.size.width / WIDTH;
double xOffset = 0;
CGRect cellFrame = CGRectMake(0, 0, cellSize, cellSize);
NSUInteger cellIndex = 0;
cellFrame.origin.x = xOffset;
for (int i = 0; i < WIDTH; i++)
{
cellFrame.origin.y = 0;
for (int j = 0; j < HEIGHT; j++, cellIndex++)
{
if([[self.levelState.boardChanges objectAtIndex:(i*HEIGHT)+j] intValue]==1){
{
NSNumber *currentCell = [self.levelState.board objectAtIndex:cellIndex];
CALayer *sublayer = [CALayer layer];
sublayer.frame = cellFrame;
if (currentCell.intValue == 1)
{
[[_layerArray objectAtIndex:(i*HEIGHT)+j ] setContents:(id) [UIImage imageNamed:#"image1.png"].CGImage];
}
else if (currentCell.intValue == 0)
{
[[_layerArray objectAtIndex:(i*HEIGHT)+j ] setContents:(id) [UIImage imageNamed:#"image2.png"].CGImage];
}
[currentLayer addSublayer:sublayer];
[_layerArray addObject:sublayer];
}
}
cellFrame.origin.y += cellSize;
}
cellFrame.origin.x += cellSize;
}
}

I would recommend a very different approach.
subclass a uiview and use it as your background
in your uiview subclass, override
-(void)drawRect:(CGRect)rect
inside, you can draw your uiimages within rectangles you define
[[UIImage imageNamed:#"image1.png"]drawInRect:CGRectMake(0,0,10,10)]; //first square
[[UIImage imageNamed:#"image2.png"]drawInRect:CGRectMake(10,0,10,10)]; // and another right beside it
you can study core graphics more to get a better idea

Related

tile arrangement of images in UIScrollView or UITableView

I have an application like photo share in which i need to display images in tile view like GOOGLE IMAGE SEARCH.
Photos should be arranged in tile manner depending upon the size of an image
For better explanation i have attached example of google image search..
And
If you have any idea about this, Please share your knowledge.
Here is the source code of an iOS library similar to what you are asking for. You can get the basic idea about how this can be done from the Source Code.
Use Scrollview.
Set two parameters, called xOffset and yOffset and UIScrollview *scrollView
int yOffset = 0;
int xOffset = 0;
for (int height=0; height < [y count]; height++)
{
for (int width=0; width < [x count]; width++)
{
NSString *imageName =
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(xOffset, yOffset, imageView.frame.size.width, imageView.frame.size.height);
[scrollView addSubview:imageView];
xOffset += imageView.frame.size.width;
}
yOffset += imageView.frame.size.height;
}
[scrollView setContentSize:CGSizeMake(xOffset,yOffset)];
Hope this helps...

How to draw to context without losing it?

I am drawing UIImages to/from a grid. I am currently only drawing the changes... and only the changes... but the old images are supposed to stay where they are... however right now they vanish after the next call of drawRect:(CGRect)rect:
- (void)drawRect:(CGRect)rect
{
int cellSize = self.bounds.size.width / WIDTH;
double xOffset = 0;
CGRect cellFrame = CGRectMake(0, 0, cellSize, cellSize);
NSUInteger cellIndex = 0;
cellFrame.origin.x = xOffset;
for (int i = 0; i < WIDTH; i++)
{
cellFrame.origin.y = 0;
for (int j = 0; j < HEIGHT; j++, cellIndex++)
{
if([[self.state.boardChanges objectAtIndex:(i*HEIGHT)+j] intValue]==1){
if (CGRectIntersectsRect(rect, cellFrame)) {
NSNumber *currentCell = [self.state.board objectAtIndex:cellIndex];
if (currentCell.intValue == 1)
{
[image1 drawInRect:cellFrame];
}
else if (currentCell.intValue == 0)
{
[image2 drawInRect:cellFrame];
}
}
}
cellFrame.origin.y += cellSize;
}
cellFrame.origin.x += cellSize;
}
}
I tried mixtures of the following without any results:
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
//CGContextAddRect(context, originalRect);
CGContextClip(context);
[image drawInRect:rect];
CGContextRestoreGState(context);
Cocoa/UIKit may discard the previously-drawn content of views whenever it likes. When your view is asked to draw, it must draw everything within the provided rect. You can't just decide to not draw some stuff. (Well, you can do whatever you like, but you get the results that you're seeing.)
Found another solution-> Just take a screenshot and draw it with drawRect as a background.
-(void)createContent{
CGRect rect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
UIGraphicsBeginImageContext(rect.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
CGImageRef screenshotRef = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
_backgroundImage = [[UIImage alloc] initWithCGImage:screenshotRef];
CGImageRelease(screenshotRef);
UIGraphicsEndImageContext();
}
- (void)drawRect:(CGRect)rect
{
[_backgroundImage drawInRect:CGRectMake(0.0f, 0.0f, self.bounds.size.width, self.bounds.size.height)];
....
....
....
}
######EDIT
Found a much more sophisticated way to do the above :
http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/
#### EDIT
Another solution would be to use CALayers and only update those parts that change...
####EDIT
OR:
self.layer.contents = (id) [_previousContent CGImage];

Using CATiledLayer to display a large image that has been programmatically split into tiles

I'm running into an issue with using CATiledLayer... I have a large image, a map, that is 4726 x 2701. Basically I'm needing to break this image into tiles and be able to zoom in to view a lot of detail, but also be able to zoom all the way out and see the entire map. Using my current implementation it works perfectly if the zoomlevel of the scrollview is set to 1.0 (the maximum zoomscale), but if you zoom out the tile are replaced incorrectly.
Here the zoomlevel is set to 1.0. The map looks perfect.
But if the zoomlevel is all the way out (0.2 I believe) the map is all messed up. I should be seeing the entire map not just two tiles.
Here is how I'm getting the tiles from the large image:
- (UIImage *)tileForScale:(CGFloat)scale row:(int)row col:(int)col {
float tileSize = 256.0f;
CGRect subRect = CGRectMake(col*tileSize, row * tileSize, tileSize, tileSize);
CGImageRef tiledImage = CGImageCreateWithImageInRect([mapImage CGImage], subRect);
UIImage *tileImage = [UIImage imageWithCGImage: tiledImage];
return tileImage;
}
I'm displaying the tiles exactly like Apple does in the PhotoScroller application.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat scale = CGContextGetCTM(context).a;
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
CGSize tileSize = tiledLayer.tileSize;
tileSize.width /= scale;
tileSize.height /= scale;
int firstCol = floorf(CGRectGetMinX(rect) / tileSize.width);
int lastCol = floorf((CGRectGetMaxX(rect)-1) / tileSize.width);
int firstRow = floorf(CGRectGetMinY(rect) / tileSize.height);
int lastRow = floorf((CGRectGetMaxY(rect)-1) / tileSize.height);
for (int row = firstRow; row <= lastRow; row++) {
for (int col = firstCol; col <= lastCol; col++) {
UIImage *tile = [self tileForScale:scale row:row col:col];
CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row,
tileSize.width, tileSize.height);
tileRect = CGRectIntersection(self.bounds, tileRect);
[tile drawInRect:tileRect];
}
}
}
Also, here is the code where I'm setting the levels of detail, which returns 4 for my image:
- (id)initWithImageName:(NSString *)name andImage:(UIImage*)image size:(CGSize)size {
self = [super initWithFrame:CGRectMake(0, 0, size.width, size.height)];
if (self) {
_imageName = [name retain];
mapImage = [image retain];
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = [self zoomLevelsForSize:mapImage.size];
}
return self;
}
- (NSUInteger)zoomLevelsForSize:(CGSize)imageSize {
int zLevels = 1;
while(YES) {
imageSize.width /= 2.0f;
imageSize.height /= 2.0f;
if(imageSize.height < 256.0 || imageSize.width < 256.0) break;
++zLevels;
}
return zLevels;
}
I'm assuming it has something to do with the tile size and the zoom scale, but I really have no clue how to solve the problem. I can't seem to find any solutions to my issue on the Google. Any help would be great! :)
You have a leak in - (UIImage *)tileForScale:(CGFloat)scale row:(int)row col:(int)col.
CGImageRef tiledImage = CGImageCreateWithImageInRect([mapImage CGImage], subRect);
Don't forget to release the CGImageRef: CGImageRelease(tiledImage);
I think there's a bug where the scale is incorrectly set in the sample code.
I have the following:
CGFloat scale = CGContextGetCTM(context).a;
scale = 1.0f / roundf(1.0f / scale);
if (scale == INFINITY) {
scale = 1.0f;
}

Having problems splitting up UIImageView into smaller images

I'm trying to take a UIImageView, resize that image and display it. Then i want to break up that image into smaller pieces and display them.
The resized image displays correctly but it appears that the "split up" images are too big; i'm thinking they are coming from the original (slightly bigger) image. The following screenshot shows the resized image on the left and the a column of split up images from the left hand side.
The fact that the resized image is displaying correctly and the smaller ones aren't has me confused. Any ideas would be appreciated - or even an alternative. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
width = imgOriginal.frame.size.width;
height = imgOriginal.frame.size.height;
whratio = width/height;
[self getOriginalImageInfo];
[self resizeImage];
resizedImage = [self resizeImage];
}
-(void) getOriginalImageInfo {
lblWidth.text = [NSString stringWithFormat:#"%0.2f", width];
lblHeight.text = [NSString stringWithFormat:#"%0.2f", height];
lblWHRatio.text = [NSString stringWithFormat:#"%0.2f", whratio];
}
-(UIImageView*) resizeImage {
if (whratio >= 0.7 && whratio <=0.89) {
imgOriginal.frame = CGRectMake(20, 20, 500, 600);
imgOriginal.autoresizingMask = NO;
float resizedWHRatio = (imgOriginal.frame.size.width)/(imgOriginal.frame.size.height);
lblResizedWidth.text = [NSString stringWithFormat:#"%0.2f", imgOriginal.frame.size.width];
lblResizedHeight.text = [NSString stringWithFormat:#"%0.2f", imgOriginal.frame.size.height];
lblResizedWHRatio.text = [NSString stringWithFormat:#"%0.2f", resizedWHRatio];
return imgOriginal;
}
return nil;
}
- (IBAction)easyPressed:(id)sender {
NSLog(#"Easy button pressed");
CGImageRef original = [resizedImage.image CGImage];
float pieceWidth = (resizedImage.frame.size.width) / 5;
float pieceHeight = (resizedImage.frame.size.height) / 6;
for (int i =0; i<6; i++) {
CGRect rectangle = CGRectMake(0, 0+((float)i*pieceHeight), pieceWidth, pieceHeight);
CGImageRef newTile = CGImageCreateWithImageInRect(original, rectangle);
UIImageView *puzzlePiece = [[UIImageView alloc] initWithFrame:CGRectMake(611, 20+((float)i*pieceHeight), pieceWidth, pieceHeight)];
puzzlePiece.tag = i+1;
puzzlePiece.image = [UIImage imageWithCGImage:newTile];
puzzlePiece.alpha = 0.5;
[puzzlePiece.layer setBorderColor: [[UIColor blackColor] CGColor]];
[puzzlePiece.layer setBorderWidth: 1.0];
[self.view addSubview:puzzlePiece];
}
}
Try applying the same scaling factor to the partial images as you are to the main image!!!
OR
Take the screenshot of the UIImageView being displayed and split that up instead.

Object leak : Adding and removing UIImageView to a UIScrollView

I'm trying to dynamically add Images to a ScrollView. Before adding I check if there are any subviews in the ScrollView and delete them in a for loop.
When I release the imageview I get a runtime error stating i'm trying to acces a deallocated instance. If I remove the release statement the application will run correctly but "build and analyze" shows a leak because th reference count of imageview is not decreased..
if ([scrollView.subviews count]>0){
for (int i=0; i<[scrollView.subviews count]; ++i) {
[[scrollView.subviews objectAtIndex:i] removeFromSuperview];
}
}
//Making a scroller with results
int scrollWidth = 0.0f;
int scrollHeight = 0.0f;
for (int i = 0; i<previewImages.count;++i)
{
UIImage *image=[previewImages objectAtIndex:i];
scrollWidth = scrollWidth + image.size.width; // Width of all the images
if (image.size.height > scrollHeight)scrollHeight= image.size.height; // Maximum image height
[image release];
}
float xC = 0.0f;
for (int i=0; i<previewImages.count; ++i) {
UIImage *image=[previewImages objectAtIndex:i];
UIImageView *imageview = [[UIImageView alloc] initWithFrame:CGRectMake(xC, 0.0f, image.size.width, image.size.height)];
UILabel *subScript = [[UILabel alloc] initWithFrame:CGRectMake(xC, image.size.height-30.0f, image.size.width,30.0f)];
subScript.textColor = [UIColor whiteColor];
subScript.backgroundColor = [UIColor colorWithWhite:0.5f alpha:0.5f];
subScript.text = [imagePaths objectAtIndex:i];
imageview.image = image;
xC = xC + image.size.width;
[image release];
[scrollView addSubview:imageview];
[imageview release];
[scrollView addSubview:subScript];
[subScript release];
}
[scrollView setContentSize:CGSizeMake(scrollWidth , scrollHeight)];
Any suggestions about the memory leak or general best practices for adding and removing views from an array into a scrollView are much appreciated!
if ([scrollView.subviews count]>0){
for (int i=0; i<[scrollView.subviews count]; ++i) {
[[scrollView.subviews objectAtIndex:i] removeFromSuperview];
}
}
is wrong, because you are removing subview from subviews array. Do reverse iteration would fix that, but I would suggest keeping the view in separated array instead.