Load images on background thread in Spritekit - uiimageview

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/

Related

Issue with loading and caching retina images for UIImageView animation

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.

Simple Animation in Cocoa Objective-C

I have written a simple animation for an iOS game and I am trying to convert it for Mac OS.
Here is my code for iOS
NSMutableArray *imgArr = [[[NSMutableArray alloc] init] autorelease];
for(int i = 1; i < 6 + 1; i++) {
[imgArr addObject: [UIImage imageNamed:[NSString stringWithFormat:#"%#%d.png", filePrefix, i]]];
}
UIImageView * animation = [[UIImageView alloc] initWithFrame:rect];
animation.animationImages = imgArr;
animation.animationRepeatCount = 1;
[animation startAnimating];
[self.view addSubview:animation];
As for cocoa, this part gives errors.. How can I fix this?
//animation.animationImages = imgArr;
//animation.animationRepeatCount = 1;
//[animation startAnimating];
New code with timer
for(int i = 0; i < 6; i++)
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(anim)
userInfo:nil
repeats:NO];
- (void)anim {
++num;
NSImageView *animation = [[NSImageView alloc] initWithFrame:NSMakeRect(shipPosition.x, shipPosition.y, shipSize.width, shipSize.height)];
[animation setImage: [NSImage imageNamed:[NSString stringWithFormat:#"explosprite%d.png", num]] ];
[objectView addSubview:animation];
[animation release];
}
UIImageView does not exist in Cocoa at all. Cocoa does not really have support for sprite animation like you're trying to do here. The most common way AFAIK is just to set an NSTimer and update the image appropriately when the timer fires.
You could also create a custom CALayer subclass that handles animating images. I'm not sure that's necessarily easier, but it's an option.

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);}

UISwipeGestureRecognizer on UIImageview inside UIView detecting wrong swipe?

I am having problem with detecting swipe on UIImageView which is inside self.view.
When I apply UISwipeGestureRecognizer on self.view it works fine, but when i try to apply same on UIImageview it detects wrong swipe direction.
Code:
{
//Swipe Up
swipeUpRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeUp:)];
swipeUpRecognizer.delegate = self;
swipeUpRecognizer = (UISwipeGestureRecognizer *)swipeUpRecognizer;
swipeUpRecognizer.numberOfTouchesRequired = 1;
swipeUpRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
[imageView addGestureRecognizer:swipeUpRecognizer];
[swipeUpRecognizer release];
//swipe Down
swipeDownRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeDown:)];
swipeDownRecognizer.delegate = self;
swipeDownRecognizer = (UISwipeGestureRecognizer *)swipeDownRecognizer;
swipeDownRecognizer.numberOfTouchesRequired = 1;
swipeDownRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
[imageView addGestureRecognizer:swipeDownRecognizer];
[swipeDownRecognizer release];
//swipe Left
swipeLeftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeLeft:)];
swipeLeftRecognizer.delegate = self;
swipeLeftRecognizer = (UISwipeGestureRecognizer *)swipeLeftRecognizer;
swipeLeftRecognizer.numberOfTouchesRequired = 1;
swipeLeftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[imageView addGestureRecognizer:swipeLeftRecognizer];
[swipeLeftRecognizer release];
//swipe Right
swipeRightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeRight:)];
swipeRightRecognizer.delegate = self;
swipeRightRecognizer = (UISwipeGestureRecognizer *)swipeRightRecognizer;
swipeRightRecognizer.numberOfTouchesRequired = 1;
swipeRightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[imageView addGestureRecognizer:swipeRightRecognizer];
[swipeRightRecognizer release];
}
You have to enable the user interaction on your UIImageView. Use the code below
imageView.userInteractionEnabled = YES;

xcode iphone - jerky scroll UITableView CellForRowAtIndexPath

Almost sorted with my 1st app, just a simple news app but when I load it onto my iPhone the scroll seems jerky can someone have a look at my function and see if i'm doing something wrong.
I need the image on the right hand side thats why i'm using custom cells.
Thanks
For any help
#define DATELABEL_TAG 1 #define MAINLABEL_TAG 2 #define PHOTO_TAG 3
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MainNewsCellIdentifier = #"MainNewsCellIdentifier";
UILabel *mainLabel, *dateLabel;
UIImageView *photo;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: MainNewsCellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: MainNewsCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
dateLabel = [[[UILabel alloc] initWithFrame:CGRectMake(15.0,15.0,170.0,15.0)] autorelease];
dateLabel.tag = DATELABEL_TAG;
dateLabel.font = [UIFont systemFontOfSize:10.0];
dateLabel.textAlignment = UITextAlignmentLeft;
dateLabel.textColor = [UIColor darkGrayColor];
dateLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin; //| UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:dateLabel];
mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake(15.0,28.0,170.0,60.0)] autorelease];
mainLabel.tag = MAINLABEL_TAG;
mainLabel.font = [UIFont boldSystemFontOfSize:14.0];
mainLabel.textColor = [UIColor blackColor];
mainLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;
mainLabel.numberOfLines = 0;
//mainLabel.backgroundColor = [UIColor greenColor];
[cell.contentView addSubview:mainLabel];
photo = [[[UIImageView alloc] initWithFrame:CGRectMake(190.0,15.0,85.0,85.0)] autorelease];
photo.tag = PHOTO_TAG;
photo.contentMode = UIViewContentModeScaleAspectFit;//UIViewContentModeScaleAspectFit; //
[cell.contentView addSubview:photo];
}
else {
dateLabel = (UILabel *)[cell.contentView viewWithTag:DATELABEL_TAG];
mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}
NSUInteger row = [indexPath row];
NSDictionary *stream = (NSDictionary *) [dataList objectAtIndex:row];
NSString *title = [stream valueForKey:#"title"];
NSString *titleString = #"";
if( ! [title isKindOfClass:[NSString class]] )
{
titleString = #"";
}
else
{
titleString = title;
}
CGSize maximumSize = CGSizeMake(180, 9999);
UIFont *dateFont = [UIFont fontWithName:#"Helvetica" size:14];
CGSize dateStringSize = [titleString sizeWithFont:dateFont
constrainedToSize:maximumSize
lineBreakMode:mainLabel.lineBreakMode];
CGRect dateFrame = CGRectMake(15.0, 28.0, 170.0, dateStringSize.height);
mainLabel.frame = dateFrame;
mainLabel.text = titleString;
dateLabel.text = [stream valueForKey:#"created"];
NSString *i = [NSString stringWithFormat:#"http://www.website.co.uk/images/%#", [stream valueForKey:#"image"]];
NSData *imageURL = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:i]];
UIImage *newsImage = [[UIImage alloc] initWithData:imageURL];
photo.image = newsImage;
[imageURL release];
[newsImage release];
return cell;
}
The problem is this:
NSString *i = [NSString stringWithFormat:#"http://www.website.co.uk/images/%#", [stream valueForKey:#"image"]];
NSData *imageURL = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:i]];
UIImage *newsImage = [[UIImage alloc] initWithData:imageURL];
You effectively say here, that as soon as the cell needs to be displayed, the image must be fetched and presented. This will cost some time - too much for a good user experience.
You should fetch the images before or while you present the table view, and cache them, e.g. in an array. Or you must handle things asynchronously, meaning that you do the loading in the background, and not wait with return cell; until the image is actually downloaded. This will be a little harder to get right.
Even if you asynchronously download images, you'll still have a jerky scroll.
Why? It's lazy image decompression. ios performs decompression at the moment it will be displayed on the screen. You have to manually decompress the images in a background thread.
Decompression does not merely mean instantiating a UIImage object. It can be somewhat complicated. The best solution, is to download SDWebImage and use the image decompressor that's included. SDWebImage will asynchronously download and perform decompression for you.
To read more about the issue see: http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/

Resources