okie i am using a PieChart i got from github Astrokin Piechart
But in this piechart label has not been added to different slices and i want something as shown below
I have looked up on stackoverflow already and am trying to draw text in draw rect but doesnt seem to work. code is below
for (int i = 0; i < slicesCount; i++)
{
double value = [self.datasource pieChartView:self valueForSliceAtIndex:i];
NSString *title = [self.datasource pieChartView:self titleForSliceAtIndex:i];
endAngle = startAngle + M_PI*2*value/sum;
/*CGFloat x = centerX + (radius+10) * (cos((startAngle-90)*M_PI/180.0) + cos((endAngle-90)*M_PI/180.0)) / 2;
CGFloat y = centerY + (radius+10) * (sin((startAngle-90)*M_PI/180.0) + sin((endAngle-90)*M_PI/180.0)) / 2;
[title drawAtPoint:CGPointMake(x, y) withFont:[UIFont fontWithName:FONT_HELVETICA size:12.0]];*/
CGFloat x = centerX + ((radius + 10) * cos(startAngle));
CGFloat y = centerY + ((radius + 10) * sin(endAngle));
// NSLog(#"X Y: %f %f %f", x, y, degree);
NSString *text = #"Test";
[text drawInRect:CGRectMake(x, y, 50, 44) withFont:[UIFont systemFontOfSize:14.0f]];
CGContextAddArc(context, centerX, centerY, radius, startAngle, endAngle, false);
UIColor *drawColor = [self.datasource pieChartView:self colorForSliceAtIndex:i];
CGContextSetStrokeColorWithColor(context, drawColor.CGColor);
CGContextSetLineWidth(context, lineWidth);
CGContextStrokePath(context);
startAngle += M_PI*2*value/sum;
}
right now it looks like this
What am i possibly doing wrong?
You draw text with
[text drawInRect:CGRectMake(x, y, 50, 44) withFont:[UIFont systemFontOfSize:14.0f]];
That makes your text's top left corner to appear at point (x,y). As I can see, you want text's center to appear at point (x,y). What you need to do is to subtract half of the text's width from x and the same for y, so that it becomes:
CGFloat height = 50.0;
CGFloat width = 44.0;
[text drawInRect:CGRectMake(x - width / 2.0, y - height / 2.0, width, height) withFont:[UIFont systemFontOfSize:14.0f]];
Also you'll need to dynamically get text's width based on the string you have, "95%" is wider than "5%", yeah? So you'll need something like:
UIFont *font = [UIFont systemFontOfSize:14.0f];
CGSize textSize = [text sizeWithFont:font]; // -sizeWithFont is deprecated in iOS7, you'll need something else
CGFloat height = textSize.height;
CGFloat width = textSize.width;
[text drawInRect:CGRectMake(x - width / 2.0, y - height / 2.0, width, height) withFont:font];
Related
I try to rotate a text along a stroke. The user can create strokes by touch and move the finger in an direction he wants, the stroke then scales to the point where the finger is. I want to show the current length along the stroke and the text show stay scale and rotate with the stroke.
I think im not so far away from the working solution. Currently the text is not always at the right position, its depends on the rotation. I think there is something wrong with Context Translation, but lets see my code.
This is my method to draw the text:
- (void)drawText:(NSString *)text withFrame:(CGRect)rect withFont:(UIFont *)font rotation:(float)radians alignment:(NSTextAlignment)alignment context:(CGContextRef)context {
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = alignment;
NSDictionary *attributes = #{ NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle,
NSForegroundColorAttributeName: [UIColor blackColor] };
CGContextSaveGState(context);
CGContextScaleCTM(context, 1.0, 1.0);
//CGRect textSize = [text boundingRectWithSize:rect.size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
CGContextTranslateCTM(context, rect.origin.x + (rect.size.width * 0.5), rect.origin.y + (rect.size.height * 0.5));
CGContextRotateCTM(context, radians);
CGContextTranslateCTM(context, -(rect.origin.x + (rect.size.width * 0.5)), -(rect.origin.y + (rect.size.height * 0.5)));
[[UIColor redColor] set];
CGContextFillRect(context, rect);
[text drawInRect:rect withAttributes:attributes];
CGContextRestoreGState(context);
}
I call it like this:
CGFloat a = shapeToBeDrawn.startPoint.y - shapeToBeDrawn.endPoint.y;
CGFloat c = shapeToBeDrawn.length;
CGFloat alpha = -asin(a/c);
CGRect r = CGRectMake(shapeToBeDrawn.startPoint.x, shapeToBeDrawn.startPoint.y - 30, shapeToBeDrawn.endPoint.x - shapeToBeDrawn.startPoint.x, 20);
[self drawText:lengthStr withFrame:r withFont:[UIFont fontWithName:#"Helvetica" size:18.0f] rotation:alpha alignment:NSTextAlignmentCenter context:context];
There im calculation the angle alpha and pass the string i want to display. And also create the frame for the text above the frame of the shape.
Here a small video how it looks currently:
Click
Hope someone can help me and my problem is clear. Thanks :)
To calculate angle properly in all quadrants, use atan2 function
alpha = atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x)
To define a rect - calculate starting coordinates for rotated rectangle:
x0 = startPoint.x + Sin(alpha) * 30
y0 = startPoint.y - Cos(alpha) * 30
rect.width = shapeToBeDrawn.length
I want to create a perfect semi circle at the bottom of my view and now I'm getting just an arc in the top of a rectangle (see attached picture). This is the code I'm using is the following where circularRect is origin = (x = 128, y = 514), size = (width = 64, height = 54)
CGFloat arcHeight = 60.0;
CGRect arcRect = CGRectMake(circularRect.origin.x, circularRect.origin.y + circularRect.size.height - arcHeight, circularRect.size.width, arcHeight);
CGFloat arcRadius = 60;
CGPoint arcCenter = CGPointMake(arcRect.origin.x + arcRect.size.width/2, arcRect.origin.y + arcRadius);
CGFloat angle = acos(arcRect.size.width / (2*arcRadius));
CGFloat startAngle = 270 * M_PI/180 + angle;
CGFloat endAngle = 90 * M_PI/180 - angle;
CGContextAddArc(context, arcCenter.x, arcCenter.y, arcHeight, startAngle, endAngle, 1);
CGContextClip(context);
CGContextClearRect(context, arcRect);
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillRect( context, arcRect);
What I'm doing wrong? Thanks!
The radius you're supplying to the arc function is too large. Your rectangle width being 64, you can only fit a circle that has a radius of 32 or less in it.
How to make UI with round image and round text, also add ratting icon on same circle. in iOS application
Import roundImageView.h class in your view class and set background image on your UIButton. Please change button type Custom.
After Following these steps try this code .
CGRect frame = CGRectMake(0, 0, 200, 200);
roundImageView *roudImage = [[roundImageView alloc]init];
UIImage *image1 = [roudImage createMenuRingWithFrame:frame :#"Your Title" labelBgColor:[UIColor colorWithRed:(191/255.f) green:(251/255.f) blue:(158/255.f) alpha:1] ringBgColor:[UIColor colorWithRed:(214/255.f) green:(214/255.f) blue:(214/255.f) alpha:1] levelUnlockShow:1 buttonObj:yourButtonObj];
[yourButtonObj setImage:image1 forState:UIControlStateNormal];
Note :- In this you can see we set only Image not background image ..
Create a class roundImageView UIImage Type and paste this code
in roundImageView.h file code
#import <UIKit/UIKit.h>
#interface roundImageView : UIImage
- (UIImage*) createMenuRingWithFrame:(CGRect)frame : (NSString*) sectionTitle labelBgColor : (UIColor*)labelBgColor ringBgColor : (UIColor *)ringBgColor levelUnlockShow: (NSInteger) levelUnloackNm buttonObj : (UIButton *)buttonObj;
Paste code in roundImageView.m file
#import "roundImageView.h"
#implementation roundImageView
#define DegreesToRadians(x) (M_PI * x / 180.0)
- (void) drawStringAtContext:(CGContextRef) context string:(NSString*) text atAngle:(float) angle withRadius:(float) radius
{
CGSize textSize = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:18]}];
float perimeter = 2 * M_PI * radius;
float textAngle = textSize.width / perimeter * 2 * M_PI;
angle += textAngle / 2;
for (int index = 0; index < [text length]; index++)
{
NSRange range = {index, 1};
NSString* letter = [text substringWithRange:range];
char* c = (char*)[letter cStringUsingEncoding:NSASCIIStringEncoding];
CGSize charSize = [letter sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:18]}];
NSLog(#"Char %# with size: %f x %f", letter, charSize.width, charSize.height);
float x = radius * cos(angle);
float y = radius * sin(angle);
float letterAngle = (charSize.width / perimeter * -2 * M_PI);
CGContextSaveGState(context);
CGContextTranslateCTM(context, x, y);
CGContextRotateCTM(context, (angle - 0.5 * M_PI));
CGContextShowTextAtPoint(context, 0, 0, c, strlen(c));
CGContextRestoreGState(context);
angle += letterAngle;
}
}
- (void)drawRect:(CGRect)rect contextData:(CGContextRef) context {
CGContextSetLineWidth(context, 30);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 50);
CGContextAddCurveToPoint(context, 0, 180, 0, 0, -80, 0);
CGContextStrokePath(context);
}
- (UIImage*) createMenuRingWithFrame:(CGRect)frame : (NSString*) sectionTitle labelBgColor : (UIColor*)labelBgColor ringBgColor : (UIColor *)ringBgColor levelUnlockShow: (NSInteger) levelUnloackNm buttonObj : (UIButton *)buttonObj
{
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
UIBezierPath *circularPath=[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, buttonObj.frame.size.width, buttonObj.frame.size.height) cornerRadius:MAX(buttonObj.frame.size.width, buttonObj.frame.size.height)];
circle.path = circularPath.CGPath;
// Configure the apperence of the circle
circle.fillColor = [UIColor blackColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 0;
buttonObj.layer.mask = circle;
CGPoint centerPoint = CGPointMake(frame.size.width / 2, frame.size.height / 2);
char* fontName = (char*)[[UIFont fontWithName:#"Helvetica" size:18].fontName cStringUsingEncoding:NSASCIIStringEncoding];
const CGFloat* ringColorComponents = CGColorGetComponents([ringBgColor CGColor]);
// const CGFloat* textBGColorComponents = CGColorGetComponents([[UIColor colorWithRed:80/255.0 green:160/255.0 blue:15/255.0 alpha:1] CGColor]) ;
const CGFloat* textColorComponents = CGColorGetComponents([[UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:1] 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, kCGEncodingMacRoman);
CGContextSetRGBStrokeColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], 1.0);
CGContextSetLineWidth(context, 25);
CGContextStrokeEllipseInRect(context, CGRectMake(10, 10, frame.size.width - (10 * 2), frame.size.height - (10 * 2)));
CGContextSetRGBFillColor(context, textColorComponents[0], textColorComponents[1], textColorComponents[2], 1.0);
CGContextSaveGState(context);
CGContextTranslateCTM(context, centerPoint.x, centerPoint.y);
// float angleStep = 2 * M_PI ;
float angle = DegreesToRadians(135);
float textRadius = 95;
textRadius = textRadius - 12;
// [self drawImageAtContext:context string:text atAngle:angle withRadius:textRadius];
[self drawLblBackGroundAtContext:context string:sectionTitle atAngle:angle withRadius:textRadius withLabelBackgroudColor:labelBgColor ];
//angle -= angleStep;
CGContextSetRGBStrokeColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], 1.0);
CGContextSetLineWidth(context, 25);
[self drawStringAtContext:context string:sectionTitle atAngle:angle withRadius:textRadius];
//angle -= angleStep;
angle = DegreesToRadians(315);
// [self drawImageAtContext:context string:text atAngle:angle withRadius:textRadius];
[self drawImageAtContext:context atAngle:angle withRadius:textRadius levelUnlock: levelUnloackNm];
//angle -= angleStep;
CGContextRestoreGState(context);
CGImageRef contextImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
//[self saveImage:[UIImage imageWithCGImage:contextImage] withName:#"test.png"];
return [UIImage imageWithCGImage:contextImage];
}
- (void) drawImageAtContext:(CGContextRef) context atAngle:(float) angle withRadius:(float) radius levelUnlock:(NSInteger )levelUnlock
{
CGSize textSize = [#"MMMMMM" sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:18]}];
float perimeter = 2 * M_PI * radius;
float textAngle = (textSize.width+1) / perimeter * 2 * M_PI;
angle += textAngle / 2;
// UIImageView *image = [[UIImageView alloc]initWithFrame:CGRectMake(angle, 0, 20, 20)];
if (levelUnlock != 0) {
for (int index = 0; index < 6; index++)
{
NSRange range = {index, 1};
NSString* letter = [#"MMMMMM" substringWithRange:range];
CGSize charSize = [letter sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:18]}];
NSLog(#"Char %# with size: %f x %f", letter, charSize.width, charSize.height);
float x = radius * cos(angle);
float y = radius * sin(angle);
float letterAngle = ((charSize.width+1) / perimeter * -2 * M_PI);
CGContextSaveGState(context);
CGContextTranslateCTM(context, x, y);
CGContextRotateCTM(context, (angle - 0.5 * M_PI));
// CGContextShowTextAtPoint(context, 0, 0, c, strlen(c));
const CGFloat* ringColorComponents;
NSInteger raiting = 6 - levelUnlock ;
if (index + 1 > raiting) {
ringColorComponents = CGColorGetComponents([[UIColor colorWithRed:0/255.0 green:170/255.0 blue:216/255.0 alpha:1] CGColor]);
}else{
ringColorComponents = CGColorGetComponents([[UIColor colorWithRed:150/255.0 green:150/255.0 blue:150/255.0 alpha:1] CGColor]);
}
CGContextSetRGBStrokeColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], 1.0);
CGContextSetRGBFillColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], 1.0);
CGContextSetLineWidth(context, 8);
//Line Changed for border
CGContextStrokeEllipseInRect(context, CGRectMake(2, 1, 8, 8));
CGContextRestoreGState(context);
angle += letterAngle;
}
}
}
- (void) drawLblBackGroundAtContext:(CGContextRef) context string:(NSString*) text atAngle:(float) angle withRadius:(float) radius withLabelBackgroudColor: (UIColor *)labelBgColor
{
// text = [NSString stringWithFormat:#"%#sdsad",text];
CGSize textSize = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:20]}];//[text sizeWithFont:[UIFont fontWithName:#"Helvetica" size:20]];
float perimeter = 2 * M_PI * radius;
float textAngle = textSize.width / perimeter * 2 * M_PI;
angle += textAngle / 2;
for (int index = 0; index < [text length]; index++)
{
NSRange range = {index, 1};
NSString* letter = [text substringWithRange:range];
// char* c = (char*)[letter cStringUsingEncoding:NSASCIIStringEncoding];
CGSize charSize = [letter sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"Helvetica" size:18]}];
NSLog(#"Char %# with size: %f x %f", letter, charSize.width, charSize.height);
float x = radius * cos(angle);
float y = radius * sin(angle);
float letterAngle = ((charSize.width+1) / perimeter * -2 * M_PI);
CGContextSaveGState(context);
CGContextTranslateCTM(context, x, y);
CGContextRotateCTM(context, (angle - 0.5 * M_PI));
const CGFloat* ringColorComponents = CGColorGetComponents([ labelBgColor CGColor]);
CGContextSetRGBStrokeColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], 1.0);
CGContextSetRGBFillColor(context, ringColorComponents[0], ringColorComponents[1], ringColorComponents[2], 1.0);
if (index + 1 == [text length]){
CGContextSetLineWidth(context, 15);
CGContextStrokeRect(context, CGRectMake(0, 2, 15, 15));
}else{
CGContextSetLineWidth(context, 15);
CGContextStrokeRect(context, CGRectMake(0, 2, 15, 15));
}
CGContextRestoreGState(context);
if (index +1 == [text length]) {
angle += letterAngle ;
}else{
angle += letterAngle;
}
}
}
#end
Try this code its working fine ...
Well,i didnt got your question completely..,if u want ur image view to be a proper circle,then use layer property.
Add QuartzCore framework to your project
#import <QuartzCore/QuartzCore.h>
then,in viewDidLoad ,add the following code.
myImageView.layer.cornerRadius = (myImageView.bounds.size.height/2);
myImageView.layer.masksToBounds = YES;
The rest is upto you,use your logic to do the remaining.
EDIT
https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CALayer_class/index.html go through these.
You can take a look on WWDC2014 videos: "What's new in interface builder."
They are creating class similar to what you need.
https://developer.apple.com/videos/wwdc/2014/
I'm drawing lines in an app by connecting two points, what I want to do next is to display a label at the middle of the line displaying its length. This is what I'm using for the length calculation:
CGFloat dx = point2.x - point1.x;
CGFloat dy = point2.y - point1.y;
return sqrt(dx*dx + dy*dy );
How could I display the distance in in or cm and how would I place the label in the middle of the line.
Thanks
EDIT: Here's my drawRect method, with what I've tried:
- (void)drawRect:(CGRect)rect
{
// Drawing code
if ([_pointsArray count] > 0) {
int i;
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(c, 7.0f);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextSetStrokeColorWithColor(c, [UIColor blueColor].CGColor);
for (i = 0; i < ([_pointsArray count]-1); i++) {
CGPoint p1 = [[self.pointsArray objectAtIndex:i]CGPointValue];
CGPoint p2 = [[self.pointsArray objectAtIndex:i+1]CGPointValue];
CGContextMoveToPoint(c, p1.x, p1.y);
CGContextAddLineToPoint(c, p2.x, p2.y);
UILabel *lengthLabel = [[UILabel alloc]init];
CGFloat dx = p2.x - p1.x;
CGFloat dy = p2.y - p1.y;
CGFloat result = sqrt(dx*dx + dy*dy);
lengthLabel.text = [NSString stringWithFormat:#"%f", result];
CGContextStrokePath(c);
}
}
}
The first thing you need to do is find where to put the text. Start by finding the center of the line. This is simple geometry based on the two points you have:
CGPoint p1 = [[self.pointsArray objectAtIndex:i]CGPointValue];
CGPoint p2 = [[self.pointsArray objectAtIndex:i+1]CGPointValue];
CGPoint lineCenter = CGPointMake((p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0);
Now you need to calculate the rectangle to draw the text.
NSString *text = [NSString stringWithFormat:#"%f", result];
UIFont *font = [UIFont systemFontWithSize:14]; // pick a size
NSDictionary *attrs = #{ NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor blueColor] }; // pick a color
CGSize *textSize = [text sizeWithAttributes:attrs];
Now you have the line center and the text size you need to calculate the text rectangle:
CGRect textRect = CGRectMake(lineCenter.x - (textSize.width / 2.0), lineCenter.y - (textSize.height / 2.0), textSize.width, textSize.height);
And now you can draw the text:
[text drawInRect:textRect withAttributes:attrs];
All together with your code:
UIFont *font = [UIFont systemFontWithSize:14]; // pick a size
NSDictionary *attrs = #{ NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor blueColor] }; // pick a color
for (i = 0; i < ([_pointsArray count]-1); i++) {
CGPoint p1 = [[self.pointsArray objectAtIndex:i]CGPointValue];
CGPoint p2 = [[self.pointsArray objectAtIndex:i+1]CGPointValue];
CGContextMoveToPoint(c, p1.x, p1.y);
CGContextAddLineToPoint(c, p2.x, p2.y);
CGFloat dx = p2.x - p1.x;
CGFloat dy = p2.y - p1.y;
CGFloat result = sqrt(dx*dx + dy*dy);
NSString *text = [NSString stringWithFormat:#"%f pts", result];
CGPoint lineCenter = CGPointMake((p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0);
CGSize *textSize = [text sizeWithAttributes:attrs];
CGRect textRect = CGRectMake(lineCenter.x - (textSize.width / 2.0), lineCenter.y - (textSize.height / 2.0), textSize.width, textSize.height);
[text drawInRect:textRect withAttributes:attrs];
CGContextStrokePath(c);
}
Notes:
This is untested. There could be typos.
This draws the line length in points. You need to find your own way to convert to other units.
How do I change the vertical alignment of the text in a CTFramesetter frame? I want my text to be in the middle instead of being at the top. I am using Core Text framework. There is a setting of the paragraph to change horizontal aligment but not vertical.
Finally figured it out ...
CGRect boundingBox = CTFontGetBoundingBox(font);
//Get the position on the y axis
float midHeight = self.frame.size.height / 2;
midHeight -= boundingBox.size.height / 2;
CGPathAddRect(path, NULL, CGRectMake(0, midHeight, self.frame.size.width, boundingBox.size.height));
Thanks Nick, that was a great snippet.
Just expanding on that, if your doing Top, Middle and Bottom alignment with an enum, for example you could do it like so:
if (VerticalAlignmentTop == currentTextAlignment) {
CGPathAddRect(path, NULL, rect); // Draw normally (top)
}
else if (VerticalAlignmentMiddle == currentTextAlignment) {
CGRect boundingBox = CTFontGetBoundingBox(fontRef);
//Get the position on the y axis (middle)
float midHeight = rect.size.height / 2;
midHeight -= boundingBox.size.height / 2;
CGPathAddRect(path, NULL, CGRectMake(0, midHeight, rect.size.width, boundingBox.size.height));
}
else {
CGRect boundingBox = CTFontGetBoundingBox(fontRef);
CGPathAddRect(path, NULL, CGRectMake(0, 0, rect.size.width, boundingBox.size.height));
}
You can use [NSString boundingRectWithSize:options:attributes:context:] to get the rectangle of your string's bounding box, which allows multi-line text as well.
In your draw text method, do the following (RECT is the rectangle where you want to draw the text):
// get the graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// flip the context coordinate
CGContextTranslateCTM(context, 0.0f, 2*RECT.origin.y+RECT.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
// Set the text matrix.
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// set text horizontal alignment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentCenter;
NSDictionary *attributes = #{NSParagraphStyleAttributeName:paragraphStyle, NSFontAttributeName:YOUR_FONT, NSForegroundColorAttributeName:TEXT_COLOR};
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:YOUR_TEXT attributes:attributes];
CGMutablePathRef path = CGPathCreateMutable();
// set text vertical alignment
CGSize textSize = [text boundingRectWithSize:RECT.size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
CGPathAddRect(path, NULL, CGRectMake(RECT.origin.x, RECT.origin.y-(RECT.size.height-textSize.height)/2.0f, RECT.size.width, RECT.size.height));
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attrString.length), path, NULL);
CTFrameDraw(frame, context);
CFRelease(frame);
CFRelease(path);
CFRelease(frameSetter);
[attrString release];
[paragraphStyle release];
CGContextRestoreGState(context);
This accounts for the fact that multiple font types and styles can be used in a frame (calculates both height and width of text, look in the if(index == lastLineIndex) block to see where the height is calculated):
- (CGSize) measureFrame: (CTFrameRef) frame forContext: (CGContext *) cgContext
{
CGPathRef framePath = CTFrameGetPath(frame);
CGRect frameRect = CGPathGetBoundingBox(framePath);
CFArrayRef lines = CTFrameGetLines(frame);
CFIndex numLines = CFArrayGetCount(lines);
CGFloat maxWidth = 0;
CGFloat textHeight = 0;
// Now run through each line determining the maximum width of all the lines.
// We special case the last line of text. While we've got it's descent handy,
// we'll use it to calculate the typographic height of the text as well.
CFIndex lastLineIndex = numLines - 1;
for(CFIndex index = 0; index < numLines; index++)
{
CGFloat ascent, descent, leading, width;
CTLineRef line = (CTLineRef) CFArrayGetValueAtIndex(lines, index);
width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
if(width > maxWidth)
{
maxWidth = width;
}
if(index == lastLineIndex)
{
// Get the origin of the last line. We add the descent to this
// (below) to get the bottom edge of the last line of text.
CGPoint lastLineOrigin;
CTFrameGetLineOrigins(frame, CFRangeMake(lastLineIndex, 1), &lastLineOrigin);
// The height needed to draw the text is from the bottom of the last line
// to the top of the frame.
textHeight = CGRectGetMaxY(frameRect) - lastLineOrigin.y + descent;
}
}
// For some text the exact typographic bounds is a fraction of a point too
// small to fit the text when it is put into a context. We go ahead and round
// the returned drawing area up to the nearest point. This takes care of the
// discrepencies.
return CGSizeMake(ceil(maxWidth), ceil(textHeight));
}
Reference: Scott Thompson (http://lists.apple.com/archives/quartz-dev/2008/Mar/msg00079.html)