Skip to content

Instantly share code, notes, and snippets.

@sorted-bits
Created October 20, 2011 20:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sorted-bits/1302242 to your computer and use it in GitHub Desktop.
Save sorted-bits/1302242 to your computer and use it in GitHub Desktop.
Placing curved text on an UIImage
NSArray* sections = [[NSArray alloc] initWithObjects:@"text1", @"text2", @"text3", @"text4", nil];
- (UIImage*) createMenuRingWithFrame:(CGRect)frame
{
// First fix the frame to make sure it uses the right scaling (iPhone 4 / iPad compatibility).
frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width * scale, frame.size.height * scale);
// Same for the text radius
float scaledTextRadius = textRadius * scale;
float scaledRingWidth = ringWidth;
// Define the centerpoint of the circle.
CGPoint centerPoint = CGPointMake(frame.size.width / 2, frame.size.height / 2);
// self.menuItemsFont is of the type UIFont, which I use to get the font for displaying the text on the circle.
char* fontName = (char*)[self.menuItemsFont.fontName cStringUsingEncoding:NSASCIIStringEncoding];
// 2 UIColor's, which I need for the color of the circle and for the text
ringColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.7];
textColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1];
CGFloat* ringColorComponents = (float*)CGColorGetComponents(ringColor.CGColor);
CGFloat* textColorComponents = (float*)CGColorGetComponents(textColor.CGColor);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, frame.size.width, frame.size.height, 8, 4 * frame.size.width, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextSelectFont(context, fontName, 18 * scale, kCGEncodingMacRoman);
CGContextSetRGBStrokeColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], ringAlpha);
CGContextSetLineWidth(context, scaledRingWidth);
CGContextStrokeEllipseInRect(context, CGRectMake(scaledRingWidth, scaledRingWidth, frame.size.width - (scaledRingWidth * 2), frame.size.height - (scaledRingWidth * 2)));
CGContextSetRGBFillColor(context, textColorComponents[0], textColorComponents[1], textColorComponents[2], textAlpha);
CGContextSaveGState(context);
CGContextTranslateCTM(context, centerPoint.x, centerPoint.y);
// define the angles of the texts to divide them evenly on the circle.
float angleStep = 2 * M_PI / [sections count];
// I set the starting-angle on 90 degrees, to make sure the first text was ON TOP of the circle.
float angle = degreesToRadians(90);
// Some custom text-radius fixing (and scaling for iPhone 4 & iPad). To make sure my text centered nicely.
scaledTextRadius = scaledTextRadius - 12 * scale;
// Loop through texts and draw them at the appropriate angle.
for (NSString* text in sections)
{
// call a method that actually draws the text at an angle
[self drawStringAtContext:context string:text atAngle:angle withRadius:scaledTextRadius];
angle -= angleStep;
}
CGContextRestoreGState(context);
CGImageRef contextImage = CGBitmapContextCreateImage(context);
// Release the stuff to avoid memory leaks.
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return [UIImage imageWithCGImage:contextImage];
}
// This method draws the text at an angle, while the location is based on the radius + angle.
- (void) drawStringAtContext:(CGContextRef) context string:(NSString*) text atAngle:(float) angle withRadius:(float) radius
{
// Scale the fontsize to match UIScreen scaling
UIFont* scaledMenuItemsFont = [menuItemsFont fontWithSize:18 * scale];
// Get the size of the string when it would be drawn with the selected font
CGSize textSize = [text sizeWithFont:scaledMenuItemsFont];
float perimeter = 2 * M_PI * radius;
// determine the angle of the text.
float textAngle = (textSize.width) / perimeter * 2 * M_PI;
angle += textAngle / 2;
// We loop through each letter in the string, so we can set the angle for each letter right
for (int index = 0; index < [text length]; index++)
{
// Get the correct letter by the index
NSRange range = {index, 1};
NSString* letter = [text substringWithRange:range];
char* c = (char*)[letter cStringUsingEncoding:NSASCIIStringEncoding];
CGSize charSize = [letter sizeWithFont:scaledMenuItemsFont];
// Determin the X and Y position of the letter based on the angle and the radius
float x = radius * cos(angle);
float y = radius * sin(angle);
float letterAngle = (charSize.width / perimeter * -2 * M_PI);
// Save the state of the context, because rotations are based on the original origin
CGContextSaveGState(context);
CGContextTranslateCTM(context, x, y);
// Rotate the canvas so we can just draw our text horizontally
CGContextRotateCTM(context, (angle - 0.5 * M_PI));
CGContextShowTextAtPoint(context, 0, 0, c, strlen(c));
// Restore the context again.
CGContextRestoreGState(context);
angle += letterAngle;
}
}
@k1123c
Copy link

k1123c commented Feb 5, 2013

Hello there, I'd like to use your code to curve text. However, I'm not quite sure how to implement your code and make it work. Where should I put "NSArray* sections = [[NSArray alloc] initWithObjects:@"text1".... " into? Into Viewdidload? gistfile1.h file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment