Anyways, during my love affair I got bit annoyed at having to subclass UIView every time i wanted to make a quick sprite, or overlay a one image on top of another.
CALayer's a are awesome and everything, but i still need to subclass UIView unless i want a bunch of layout mess every where.
So I thought wouldn't it be cool to be able to create UIImage's quickly and then just throw them into a CALayers or UIImageView's and worry about the rest later.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <UIKit/UIKit.h> | |
#import <QuartzCore/QuartzCore.h> | |
typedef void(^UIImageRenderBlock)(CGContextRef context); | |
@interface UIImage (render) | |
+(UIImage *)imageWithSize:(CGSize) canvasSize block:(UIImageRenderBlock) aBlock; | |
@end | |
@implementation UIImage (render) | |
+(UIImage *)imageWithSize:(CGSize) canvasSize block:(UIImageRenderBlock) aBlock { | |
CGContextRef context; | |
void *bitmapData; | |
CGColorSpaceRef colorSpace; | |
int bitmapByteCount; | |
int bitmapBytesPerRow; | |
CGImageRef resultImage; | |
UIImage *image; | |
// | |
bitmapBytesPerRow = canvasSize.width * 4; | |
bitmapByteCount = (bitmapBytesPerRow * canvasSize.height); | |
//Create the color space | |
colorSpace = CGColorSpaceCreateDeviceRGB(); | |
bitmapData = malloc( bitmapByteCount ); | |
//Check the the buffer is alloc'd | |
if( bitmapData == NULL ){ | |
NSLog(@"Buffer could not be alloc'd"); | |
} | |
//Create the context | |
context = CGBitmapContextCreate(bitmapData, canvasSize.width, canvasSize.height, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); | |
if( context == NULL ){ | |
NSLog(@"Context could not be created"); | |
} | |
//Render user data | |
aBlock(context); | |
//The contents of the context could be saved out as follows | |
//Get the result image | |
resultImage = CGBitmapContextCreateImage(context); | |
//Save the image | |
image = [UIImage imageWithCGImage:resultImage]; | |
//Cleanup | |
CGImageRelease(resultImage); | |
free(bitmapData); | |
CGColorSpaceRelease(colorSpace); | |
return image; | |
} | |
@end | |
The above does just that, you give it a canvas size or more informally the size of the UIImage that you wish to generate.
It will create the CGBitmapContext with a RGB colorspace, and then you can draw to your hearts content.
You can treat the block almost like a you would drawRect. I say almost because it's not inserted into the graphics context stack, so some of the helper methods like [[UIColor blackColor] setFill] will not work, and you'll have to learn how to do it the 'proper way' (CGColorSetFillColor()). Just consider it a general rule, that if it renders something and doesn't take a CGContextRef you need an alternative method to use it inside this block.