Issue with loading and caching retina images for UIImageView animation - uiimageview

What I have
For my game I'm creating several animations using view.animationImages = imagesArray; [view startAnimating];
In my animation helper class I use this
- (UIImage *)loadRetinaImageIfAvailable:(NSString *)path
{
NSString *retinaPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:#"%##2x.%#", [[path lastPathComponent] stringByDeletingPathExtension], [path pathExtension]]];
if ([UIScreen mainScreen].scale == 2.0 && [[NSFileManager defaultManager] fileExistsAtPath:retinaPath] == YES)
return [[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:retinaPath]] CGImage] scale:2.0 orientation:UIImageOrientationUp];
else
return [UIImage imageWithContentsOfFile:path];
}
- (NSMutableArray *)generateCachedImageArrayWithFilename:(NSString *)filename extension:(NSString *)extension andImageCount:(int)count
{
_imagesArray = [[NSMutableArray alloc] init];
_fileExtension = extension;
_imageName = filename;
_imageCount = count;
for (int i = 0; i < count; i++)
{
NSString *tempImageNames = [NSString stringWithFormat:#"%#%i", filename, i];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:tempImageNames ofType:extension];
UIImage *frameImage = [self loadRetinaImageIfAvailable:imagePath];
UIGraphicsBeginImageContext(frameImage.size);
CGRect rect = CGRectMake(0, 0, frameImage.size.width, frameImage.size.height);
[frameImage drawInRect:rect];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_imagesArray addObject:renderedImage];
if (_isDoublingFrames)
{
[_imagesArray addObject:renderedImage];
}
else if (_isTriplingFrames)
{
[_imagesArray addObject:renderedImage];
[_imagesArray addObject:renderedImage];
}
NSLog(#"filename = %#", filename);
}
return _imagesArray;
}
Problem and facts
Without caching images I got retina versions of images byt my animations are not fluent
If I cache images this way, animations are ok, but uses non-retina versions of images
Please is there some other way to cache and get retina versions?

Are these images located in your application bundle? If so this logic is all unnecessary because the imageWithContentsOfFile: method will already detect and load the #2x image on a retina device.
Also, you'd be better off using the [UIImage imageNamed:] method instead because this automatically caches the image for re-use, and decompresses it immediately, avoiding the need for manually caching the images, or for that trickery with drawing it into an offscreen CGContext.

Related

Cocos2d V3 can't switch scenes after using the UIImagePickerController Class

I'm having a little trouble switching scenes (or even displaying sprites) on my CCscene UserLevelCreation, but it only happens after i use the UIImagePickerController to take/accept a picture, And then when I try to switch scenes, It crashes with the error:
"Could not attach texture to framebuffer"
this is the code I use to take a picture:
-(void)takePhoto{
AppController *appdel = (AppController*) [[UIApplication sharedApplication] delegate];
#try {
uip = [[UIImagePickerController alloc] init] ;
uip.sourceType = UIImagePickerControllerSourceTypeCamera;
uip.allowsEditing = YES;
uip.delegate = self;
}
#catch (NSException * e) {
uip = nil;
}
#finally {
if(uip) {
[appdel.navController presentModalViewController:uip animated:NO];
}
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSString * LevelImage;
LevelImage=[info objectForKey:UIImagePickerControllerOriginalImage];
AppController *appdel = (AppController*) [[UIApplication sharedApplication] delegate];
[appdel.navController dismissModalViewControllerAnimated:YES];
[NSThread detachNewThreadSelector:#selector(writeImgToPath:) toTarget:self withObject:LevelImage];
}
And the WriteImageToPath function:
-(void)writeImgToPath:(id)sender
{
UIImage *image = sender;
NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
CGSize size;
int currentProfileIndex = 1;
NSString *path = [[pathArr objectAtIndex:0]
stringByAppendingPathComponent:[NSString stringWithFormat:#"CustomLevel_%d.png",currentProfileIndex]];
size = CGSizeMake(1136, 640);
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, 1136, 640)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
CCLOG(#"saved...");
CGRect r = CGRectMake(0, 0, 1136, 640);
UIGraphicsBeginImageContext(r.size);
UIImage *img1;
[image drawInRect:r];
img1 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(img1, nil, nil, nil);
currentProfileIndex++;
[[CCDirector sharedDirector] replaceScene:[LevelEditor Scene]
withTransition:[CCTransition transitionPushWithDirection:CCTransitionDirectionLeft duration:1.0f]];
}
-UPDATE----------------------------------------------------------------------------------
I just tried switching scenes in different places in the code, and I can switch scenes freely until the Function:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
I think that function might be the problem.
So far, I have tried to:
1.switch scenes before taking the picture, and that works fine
2.I read that it might be because i was resizing the image, but commenting that out made no difference.
3.I've also added breakpoints before the scene switch, and the scene isn't nil
4.Finally i tred dismissing the uip UIImagePickerController, but that made no difference either.
Sorry for the long post /: if anyone knows whats going on any help would be really great.
Ok so I found the problem to be this line:
[NSThread detachNewThreadSelector:#selector(writeImgToPath:) toTarget:self withObject:LevelImage];
}
Apparently some Parts of Uikit are not thread-friendly, so I'm now using just
[self writeImgToPath];
And it works, hope this helps anyone with the same problem.

Load images on background thread in Spritekit

Right guys, I have scroll view in my SKScene running from a SKAction with the following code -
for (int i=0; i<[listOfImages count]; i++) {
NSDictionary *myDic = [listOfImages objectAtIndex:i];
NSString *urlImage = [myDic objectForKey:#"product_image"];
NSURL *imageURL = [NSURL URLWithString:urlImage];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *cacheImage = [UIImage imageWithData:imageData];
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(SCREEN_WIDTH/4+leftMargin, 0, cacheImage.size.width/2, cacheImage.size.height/2)];
[image setImage:cacheImage];
image.tag = tag;
image.contentMode = UIViewContentModeScaleAspectFit;
[scrollView addSubview:image];
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
recognizer.numberOfTapsRequired = 1;
recognizer.numberOfTouchesRequired = 1;
recognizer.delegate = self;
[image addGestureRecognizer:recognizer];
[image setUserInteractionEnabled:YES];
leftMargin += SCREEN_WIDTH;
tagValue += 1;
tag += 1;
}
I need to understand how to load the images in the background as there are almost 140 images to load and its taking 5 minutes to load everything and show. Please help me out !!
You could have a queue where one load ending triggers another, as opposed to triggering all the loads at once.
I did a google search and got this if you need help with applying the concept :
http://khanlou.com/2012/08/asynchronous-downloaded-images-with-caching/

How to draw EPS data on NSView

I'm struggling with the problem to draw an eps file on a NSView.
When I first load the eps file from a file and draw it with drawInRect: the image is displayed correctly. However, the image will not be drawn when I load it from an archive file.
I've prepared a dirty small example that you can copy/paste and try out. Create a new Cocoa App project and add this to the delegate method.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Just a sample eps file
NSURL *url = [NSURL URLWithString: #"http://embedded.eecs.berkeley.edu/concurrency/latex/figure.eps"];
NSImage *epsImage = [[[NSImage alloc] initWithContentsOfURL: url] autorelease];
// Encode data
NSMutableData *mutableData = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: mutableData] autorelease];
[coder encodeObject: epsImage forKey: #"image"];
[coder finishEncoding];
NSString *dataFile = [#"~/Desktop/image.data" stringByExpandingTildeInPath];
[mutableData writeToFile: dataFile atomically: YES];
// Decode data
NSData *data = [NSData dataWithContentsOfFile: dataFile];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
NSImage *loadedImage = [decoder decodeObjectForKey: #"image"];
// Draw image
NSRect rect;
rect.origin = NSZeroPoint;
rect.size = loadedImage.size;
NSView *view = [[NSApp mainWindow] contentView];
[view lockFocus];
[loadedImage drawInRect: rect fromRect: rect operation: NSCompositeSourceOver fraction: 1.0];
[view unlockFocus];
}
To prove that the first loaded image draws correctly just change the line [loadedImage drawInRect:...] to [epsImage drawInRect:...].
I'm using NSKeyedArchiver and NSKeyedUnarchiver here for simulating encodeWithCoder: and initWithCoder:. So please focus on the fact that NSImage with NSEPSImageRep representation, which does not contain a preview (from a resource fork?) and loaded purely as eps commands, is not drawn on a NSView correctly.
Any help is appreciated.
Due to the way that cacheing works on NSImage, I've often found it more effective to actually grab the NSImageRep if I know what the type is.
In our code, we found that the most reliable way to save off images is in their original format, but that requires either saving off the data in its original format somewhere else, or requesting the data from the NSImageRep. Unfortunately, there's not a generic -(NSData*)data method of NSImageRep, so we ended up specifically checking for various types of NSImageRep and saving them off depending on what we knew them to be.
Fortunately, loading is simple, as NSImage::initWithData: will figure out the type based on the data.
Here's our long-standing code for doing this. Basically, it prefers PDF then EPS then it makes a TIFF of anything it doesn't understand.
+ (NSData*) dataWithImage:(NSImage*)image kindString:( NSString**)kindStringPtr
{
if (!image)
return nil;
NSData *pdfData=nil, *newData=nil, *epsData=nil, *imageData=nil;;
NSString *kindString=nil;
NSArray *reps = [image representations];
for (NSImageRep *rep in reps) {
if ([rep isKindOfClass: [NSPDFImageRep class]]) {
// we have a PDF, so save that
pdfData = [(NSPDFImageRep*)rep PDFRepresentation];
PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfData];
newData = [doc dataRepresentation];
if (newData && ([newData length]<[pdfData length])) {
pdfData = newData;
}
break;
}
if ([rep isKindOfClass: [NSEPSImageRep class]]) {
epsData = [(NSEPSImageRep*)rep EPSRepresentation];
break;
}
}
if (pdfData) {
imageData=pdfData;
kindString= #"pdfImage";
} else if (epsData) {
imageData=epsData;
kindString=#"epsImage";
} else {
// make a big copy
NSBitmapImageRep *rep0 = [reps objectAtIndex:0];
if ([rep0 isKindOfClass: [NSBitmapImageRep class]]) {
[image setSize: NSMakeSize( [rep0 pixelsWide], [rep0 pixelsHigh])];
}
imageData = [image TIFFRepresentation];
kindString=#"tiffImage";
}
if (kindStringPtr)
*kindStringPtr=kindString;
return imageData;
}
Once we have the NSData*, it can be saved in a keyed archive, written to disk or whatever.
On the way back in, load in the NSData* and then
NSImage *image = [[NSImage alloc] initWithData: savedData];
and you should be all set.

Manage memory allocation issue on Map iOS

In my iPhone app I have a map view. In this I am displaying a number of pin views depends on the data from a web server. Here is the method I used for it.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSString *identifier = #"myPin";
self.pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (self.pinView == nil) {
self.pinView= [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier]autorelease]; //11.1%
} else {
self.pinView.annotation = annotation;
}
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; //20.4%
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
self.pinView.rightCalloutAccessoryView = rightButton; //2.7%
MyAnnotation *annot = (MyAnnotation*)annotation;
UIImageView *egoimageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"defaultPerson"]]; //17.5%
NSString *imageUrl = [NSString stringWithFormat:#"%#%#", CommonImageURL, [friendsProfileImageArray objectAtIndex:annot.tag]]; //9.4%
if ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)] &&
([UIScreen mainScreen].scale == 2.0)) {
// Retina display
[egoimageView setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:#"defaultPerson#2x.png"]]; //2.6%
egoimageView.image = [UIImage imageWithCGImage:egoimageView.image.CGImage scale:egoimageView.image.size.width/25 orientation:egoimageView.image.imageOrientation];
} else {
// non-Retina display
[egoimageView setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:#"defaultPerson.png"]]; //14.6%
egoimageView.image = [UIImage imageWithCGImage:egoimageView.image.CGImage scale:egoimageView.image.size.width/25 orientation:egoimageView.image.imageOrientation]; //1.6%
}
[egoimageView sizeToFit];
self.pinView.leftCalloutAccessoryView = egoimageView; //3.1%
[egoimageView release];
self.pinView.canShowCallout=YES;
self.pinView.animatesDrop=YES;
//pin color based on status.....
if ([annot.relationshipStatus intValue]==2 )
self.pinView.pinColor=MKPinAnnotationColorGreen;
else
self.pinView.pinColor=MKPinAnnotationColorPurple; //17.1%
return self.pinView;
}
In this I have mentioned the percentage of memory allocations. If I continuously load the map, the total memory usage increases and the app crashes. How can I properly fix this? I have tried to fix it, but I don't know what more I can do.
Please help, thanks in advance.

Xcode: ScrollView & image array

I have an array with images presented in a scrollView. It works perfectly on the simulator however when i run it on the actual device (1st gen iPad) it crashes. At first i lowered the quality of the images to the lowest possible (<120kb each) but that didn't solved my problem. Then i reduced the number of images from 13 to 6 and it works. Is there any work you can think of?
Below is the code i use:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
images = [NSArray arrayWithObjects:[UIImage imageNamed:#"back1.jpg"], [UIImage imageNamed:#"back2.jpg"], [UIImage imageNamed:#"back3.jpg"], [UIImage imageNamed:#"back4.jpg"], [UIImage imageNamed:#"back5.jpg"], [UIImage imageNamed:#"back6.jpg"], [UIImage imageNamed:#"back7.jpg"], [UIImage imageNamed:#"back8.jpg"],[UIImage imageNamed:#"back9.jpg"],[UIImage imageNamed:#"back10.jpg"],[UIImage imageNamed:#"back11.jpg"],[UIImage imageNamed:#"back12.jpg"],[UIImage imageNamed:#"back13.jpg"], nil];
pageControl.numberOfPages=images.count;
for (int i = 0; i < images.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
subview = [[UIImageView alloc] initWithFrame:frame];
subview.contentMode = UIViewContentModeScaleAspectFit;
subview.image = [images objectAtIndex:i]; //problima sthn eikona
[self.scrollView addSubview:subview];
}
pageControlBeingUsed = NO;
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * images.count, self.scrollView.frame.size.height);}

Resources