I use UIKit for my UI, but one issue I was facing is the sheer number of separate image files I was building up in my project. Texture atlas generation is common in OpenGL libraries like cocos2d, and TexturePacker has an export option specifically for these libraries. I couldn’t find anything for UIKit however.

I wrote a quick function to parse TexturePacker’s “Generic XML” data format and generate an NSDictionary of UIImages.

It depends on XMLReader, so you’ll need to include that in your project too.

Updates

  • 13/01/13 – Fixed unnecessary allocation of UIImage which caused a leak.

UIImage+Sprite.h

// Created by Daniel Sefton, 2012
// Do what you want license
 
#import <Foundation/Foundation.h>
 
/**
 UIImage category to handle parsing of TexturePacker's Generic XML format.
 */
@interface UIImage (Sprite)
 
/**
 The method returns a dictionary of UIImages. Use this function once and reference its contents.
 @param filename the XML file to load, which should be added to your project's bundle
 @returns dictionary of UIImages
 */
+ (NSDictionary*)spritesWithContentsOfFile:(NSString*)filename;
 
@end

UIImage+Sprite.m

// Created by Daniel Sefton, 2012
// Do what you want license
 
#import "UIImage+Sprite.h"
#import "XMLReader.h"
 
@implementation UIImage (Sprite)
 
+ (NSDictionary*)spritesWithContentsOfFile:(NSString*)filename
{
	NSString* file = [[filename lastPathComponent] stringByDeletingPathExtension];
	if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && 
		([UIScreen mainScreen].scale == 2.0))
	{
		file = [NSString stringWithFormat:@"%@@2x", file];
	}
	NSString* extension = [filename pathExtension];
	NSData* data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:extension]];
	NSError* error = nil;
	NSDictionary* xmlDictionary = [XMLReader dictionaryForXMLData:data error:&error];
	NSDictionary* xmlTextureAtlas = [xmlDictionary objectForKey:@"TextureAtlas"];
	UIImage* image = [UIImage imageNamed:[xmlTextureAtlas objectForKey:@"imagePath"]];
	CGSize size = CGSizeMake([[xmlTextureAtlas objectForKey:@"width"] integerValue], 
		[[xmlTextureAtlas objectForKey:@"height"] integerValue]);
 
	if (!image || CGSizeEqualToSize(size, CGSizeZero)) return nil;
	CGImageRef spriteSheet = [image CGImage];
	NSMutableDictionary* tempDictionary = [[[NSMutableDictionary alloc] init] autorelease];
 
	NSArray* xmlSprites = [xmlTextureAtlas objectForKey:@"sprite"];
	for (NSDictionary* xmlSprite in xmlSprites)
	{
		CGImageRef sprite = CGImageCreateWithImageInRect(spriteSheet, CGRectMake(
			[[xmlSprite objectForKey:@"x"] integerValue], 
			[[xmlSprite objectForKey:@"y"] integerValue], 
			[[xmlSprite objectForKey:@"w"] integerValue], 
			[[xmlSprite objectForKey:@"h"] integerValue]));
		[tempDictionary setObject:[UIImage imageWithCGImage:sprite] forKey:[xmlSprite objectForKey:@"n"]];
		CGImageRelease(sprite);
	}
 
    return [NSDictionary dictionaryWithDictionary:tempDictionary];
}
 
@end

Usage

self.mySprites = [UIImage spritesWithContentsOfFile:@"mysprites.xml"];
 
UIImage* myImage = [self.mySprites objectForKey:@"myimage"];

Tagged with:
 

5 Responses to Texture Atlases for UIKit with TexturePacker

  1. zeiteisen says:

    This is great. I added a repo on github with a working xcodeproj

    https://github.com/zeiteisen/Texture-Atlases-for-UIKit-with-TexturePacker-

  2. AndreasLoew says:

    Hi,

    nice job ;-) I’ll link to it from codeandweb.com
    The only thing I see missing is support for rotated sprites.

    Cheers
    Andreas

  3. Very cool. Thank you. Don’t ‘spose there is a way to get Interface Builder to use recognise the sprite images?

  4. Bach says:

    That’s pretty cool. Having the same problem with tons of resources and getting hard to manage in the project.

    So basically it makes sense to use a spritesheet all graphics it contains where to appear on that single screen right?

    How are you actually using it with UIKit, 1 spritesheet for entire app? have you have any memory issues or overhead?

    Thanks Daniel!

    • Daniel says:

      Hi Bach,

      Yes, the idea is that you bunch together multiple images in one spritesheet. It’s up to you if you want to use multiple spritesheets, and that may even be necessary if you have so many. I wouldn’t go any larger than 2048×2048 per spritesheet.

      By default the function pre-allocates all the UIImages at once, which will use more memory but reduce the number of runtime allocations, which is often preferable. I really don’t think it’s an issue, but if you want you can check it out in Instruments.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>