adding ellipsis to NSString - iphone

I have the following code, which I am trying to draw using core text and that's why I can't clip the text like what UILabel does. in other words I have to figure out the ellipsis ('...') my self.
CGSize commentSize = [[self.sizeDictionary_ valueForKey:commentSizeKey] CGSizeValue];
CGSize actualSize = [[self.sizeDictionary_ valueForKey:actualCommentSizeKey] CGSizeValue];
NSString *actualComment = self.highlightItem_.comment;
if (actualSize.height > commentSize.height){
actualComment = [self.highlightItem_.comment stringByReplacingCharactersInRange:NSMakeRange(68, 3) withString:#"..."];
}
I am having a difficult time finding the range of where the '...' based on a CGSize. What would be the best way in figuring this out?
Here's how I am drawing it:
CFStringRef string = CFBridgingRetain(actualComment);
CFMutableAttributedStringRef comment = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (comment ,CFRangeMake(0, 0), string);
CGColorRef blue = CGColorRetain([UIColor colorWithRed:131/255.f green:204/255.f blue:253/255.f alpha:1.0].CGColor);
CGColorRef gray = CGColorRetain([UIColor colorWithWhite:165/255.f alpha:1.0].CGColor);
CFAttributedStringSetAttribute(comment, CFRangeMake(0, [name length]),kCTForegroundColorAttributeName, blue);
CFAttributedStringSetAttribute(comment, CFRangeMake([name length], [self.highlightItem_.comment length] - [name length]),kCTForegroundColorAttributeName, gray);
CGColorRelease (blue);
CGColorRelease (gray);
CTFontRef nameFont = CTFontCreateWithName(CFBridgingRetain(kProximaNovaBold), 13.0f, nil);
CFAttributedStringSetAttribute(comment,CFRangeMake(0, [name length]),kCTFontAttributeName,nameFont);
CTFontRef commentFont = CTFontCreateWithName(CFBridgingRetain(kProximaNovaRegular), 13.0f, nil);
CFAttributedStringSetAttribute(comment, CFRangeMake([name length], [self.highlightItem_.comment length] - [name length]),kCTFontAttributeName,commentFont);
CGFloat commentYOffset = floorf((self.commentHeight_ - commentSize.height)/2);
CGContextSaveGState(context);
CGRect captionFrame = CGRectMake(0, 0, rect.size.width - 80, commentSize.height);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(comment);
CGMutablePathRef captionFramePath = CGPathCreateMutable();
CGPathAddRect(captionFramePath, NULL, captionFrame);
CTFrameRef mainCaptionFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), captionFramePath, NULL);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, self.buttonSize_ + 25, self.imageHeight_ + self.commentHeight_ + 6 - commentYOffset);
CGContextScaleCTM(context, 1.0, -1.0);
CTFrameDraw(mainCaptionFrame, context);
CGContextRestoreGState(context);

EDIT
(My original answer here wasn't useful; it didn't handle multiple lines. If anyone wants to see it for historical interest, look in the edit history. I've deleted it since it causes more confusion than it solves. The current answer is correct code.)
What you need to do is let CTFramesetter work out all the lines except the last one. Then you can truncate the last one by hand if required.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGRect pathRect = CGRectMake(50, 200, 200, 40);
CGPathRef path = CGPathCreateWithRect(pathRect, NULL);
CFAttributedStringRef attrString = (__bridge CFTypeRef)[self attributedString];
// Create the framesetter using the attributed string
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
// Create a single frame using the entire string (CFRange(0,0))
// that fits inside of path.
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
// Draw the lines except the last one
CFArrayRef lines = CTFrameGetLines(frame);
CFIndex lineCount = CFArrayGetCount(lines);
CGPoint origins[lineCount]; // I'm assuming that a stack variable is safe here.
// This would be bad if there were thousdands of lines, but that's unlikely.
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
for (CFIndex i = 0; i < lineCount - 1; ++i) {
CGContextSetTextPosition(context, pathRect.origin.x + origins[i].x, pathRect.origin.y + origins[i].y);
CTLineDraw(CFArrayGetValueAtIndex(lines, i), context);
}
///
/// HERE'S THE INTERESTING PART
///
// Make a new last line that includes the rest of the string.
// The last line is already truncated (just with no truncation mark), so we can't truncate it again
CTLineRef lastLine = CFArrayGetValueAtIndex(lines, lineCount - 1);
CFIndex lastLocation = CTLineGetStringRange(lastLine).location;
CFRange restRange = CFRangeMake(lastLocation, CFAttributedStringGetLength(attrString) - lastLocation);
CFAttributedStringRef restOfString = CFAttributedStringCreateWithSubstring(NULL, attrString, restRange);
CTLineRef restLine = CTLineCreateWithAttributedString(restOfString);
// We need to provide the truncation mark. This is an ellipsis (Cmd-semicolon).
// You could also use "\u2026". Don't use dot-dot-dot. It'll work, it's just not correct.
// Obviously you could cache this…
CTLineRef ellipsis = CTLineCreateWithAttributedString((__bridge CFTypeRef)
[[NSAttributedString alloc] initWithString:#"…"]);
// OK, now let's truncate it and draw it. I'm being a little sloppy here. If ellipsis could possibly
// be wider than the path width, then this will fail and truncateLine will be NULL and we'll crash.
// Don't do that.
CTLineRef truncatedLine = CTLineCreateTruncatedLine(restLine,
CGRectGetWidth(pathRect),
kCTLineTruncationEnd,
ellipsis);
CGContextSetTextPosition(context, pathRect.origin.x + origins[lineCount - 1].x, pathRect.origin.y + origins[lineCount - 1].y);
CTLineDraw(truncatedLine, context);
CFRelease(truncatedLine);
CFRelease(ellipsis);
CFRelease(restLine);
CFRelease(restOfString);
CFRelease(frame);
CFRelease(framesetter);
CGPathRelease(path);
}

How about something like this...
- (NSString *)truncate:(NSString *)string toWidth:(CGFloat)width withFont:(UIFont *)font {
CGSize size = [string sizeWithFont:font];
if (size.width <= width) return string;
NSString *truncatedString = [string copy];
NSString *ellipticalString = [truncatedString stringByAppendingString:#"..."];
size = [ellipticalString sizeWithFont:font];
while (size.width > width && truncatedString.length) {
truncatedString = [truncatedString substringToIndex:(truncatedString.length-1)];
ellipticalString = [truncatedString stringByAppendingString:#"..."];
size = [ellipticalString sizeWithFont:font];
}
return ellipticalString;
}

The easiest and simplest way,
NSString *theText = #"bla blah bla bhla bla bla";
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByTruncatingTail];
[theText drawInRect:dirtyRect withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:style, NSParagraphStyleAttributeName,nil]];
for more

Related

What will be size of UiView for drawing pdf File for A4 size paper?

Could anyone help me out what would be the size of UIView For Drawing pdf for A4 size paper.
I am using the below size but my pdfView objects like labels, images not in the size that I provide for drawing.
I create .xib and draw manually UILabels, Images, and lines Then using tag property I am provide frame for drawing but they draw different location according to .xib I am providing same size that I have in .xib (0, 0, 792, 1122).
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 792, 1122), nil);
+(void)drawLabels:(NSMutableArray*)arr isData:(BOOL)isdata
{
NSArray* objects = [[NSBundle mainBundle] loadNibNamed:#"PDFView" owner:nil options:nil];
UIView* mainView = [objects objectAtIndex:0];
if (isdata)
{
for (int i=1;i<=[arr count];i++)
{
UILabel *lbl=(UILabel*)[mainView viewWithTag:i];
[self drawText:[arr objectAtIndex:i-1] inFrame:lbl.frame isData:isdata];
}
}
}
//Here is my Draw Method
+(void)drawText:(NSString*)textToDraw inFrame:(CGRect)frameRect isData:(BOOL)isdata
{
int length=[textToDraw length];
CFStringRef string = (__bridge CFStringRef) textToDraw;
CFMutableAttributedStringRef currentText = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CTFontRef font = CTFontCreateWithName((CFStringRef)#"Helvetica Neue Bold", 12.0f, nil);
if (isdata)
{
font = CTFontCreateWithName((CFStringRef)#"Helvetica Neue ", 12.0f, nil);
}
CFAttributedStringReplaceString (currentText,CFRangeMake(0, 0), string);
CFAttributedStringSetAttribute(currentText,CFRangeMake(0, length),kCTFontAttributeName,font);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
CFRange currentRange = CFRangeMake(0, 0);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, (-1)*frameRect.origin.y*2);
CFRelease(frameRef);
CFRelease(framesetter);
}
// For Line Drawing I am using Below Code
for (int j=1001;j<=1003;j++)
{
UILabel *lbl=(UILabel*)[mainView viewWithTag:j];
CGPoint lblPoint=CGPointMake(lbl.frame.origin.x, lbl.frame.origin.y);
CGPoint lblPoint2=CGPointMake(lbl.frame.origin.x+lbl.frame.size.width, lbl.frame.origin.y);
[self drawLineFromPoint:lblPoint toPoint:lblPoint2];
}
+(void)drawLineFromPoint:(CGPoint)from toPoint:(CGPoint)to
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {0.2, 0.3, 0.2, 0.4};
CGColorRef color = CGColorCreate(colorspace, components);
CGContextSetStrokeColorWithColor(context, color);
CGContextMoveToPoint(context, from.x, from.y);
CGContextAddLineToPoint(context, to.x, to.y);
CGContextStrokePath(context);
CGColorSpaceRelease(colorspace);
CGColorRelease(color);
}
Here is My .xib
Here is my .pdf File after Drawing
If have downloaded the project from http://www.raywenderlich.com/6818/how-to-create-a-pdf-with-quartz-2d-in-ios-5-tutorial-part-2, and it seems that the CTM calculations in the drawText: method can be improved. The problem is that the height of the rect is not taken into account when modifying the transformation matrix.
If you replace
CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2);
...
CGContextTranslateCTM(currentContext, 0, (-1)*frameRect.origin.y*2);
with
CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2 + frameRect.size.height);
...
CGContextTranslateCTM(currentContext, 0, (-1)*frameRect.origin.y*2 - frameRect.size.height);
then the result seems to be more accurate.
Also the Xcode static analyzer reports a memory leak of currentText in that method, so you should add
CFRelease(currentText);
add the end of drawText:.
Do this:
NSArray *aArrPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) ;
NSString *aStrPrintPdfPath = [[aArrPaths objectAtIndex:0] stringByAppendingPathComponent:#"MyDocument.pdf"];
CGContextRef aCgPDFContextRef = [self createPDFContext:CGRectMake(0, 0, 612, 892) path:(__bridge_retained CFStringRef)aStrPrintPdfPath];
CGContextBeginPage(aCgPDFContextRef,nil);
//turn PDF upsidedown
CGAffineTransform aCgAffTrans = CGAffineTransformIdentity;
aCgAffTrans = CGAffineTransformMakeTranslation(0,892);
aCgAffTrans = CGAffineTransformScale(aCgAffTrans, 1.0, -1.0);
CGContextConcatCTM(aCgPDFContextRef, aCgAffTrans);
//capture whole view screenshot
[self.view.layer renderInContext:aCgPDFContextRef]; //add QuartzCore framework and import it
[aPageNote release];
CGContextEndPage (aCgPDFContextRef);
CGContextRelease (aCgPDFContextRef);
NSLog(#"Pdf Successfully Created");
Also add this method:
-(CGContextRef) createPDFContext:(CGRect)aCgRectinMediaBox path:(CFStringRef) aCfStrPath
{
CGContextRef aCgContextRefNewPDF = NULL;
CFURLRef aCfurlRefPDF;
aCfurlRefPDF = CFURLCreateWithFileSystemPath (NULL,aCfStrPath,kCFURLPOSIXPathStyle,false);
if (aCfurlRefPDF != NULL) {
aCgContextRefNewPDF = CGPDFContextCreateWithURL (aCfurlRefPDF,&aCgRectinMediaBox,NULL);
CFRelease(aCfurlRefPDF);
}
return aCgContextRefNewPDF;
}

CTFrameGetLineOrigins Got Incorrect Origins

I Use CTFrameGetLineOrigins for get the origin of each line. But it always returns wrong line origin.
The code:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CTFontRef font = CTFontCreateUIFontForLanguage(kCTFontUserFontType, self.font.pointSize, NULL);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTForegroundColorAttributeName, self.textColor.CGColor);
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFAttributedStringGetLength(attrString)), kCTFontAttributeName, font);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CGPathRef path = CGPathCreateWithRect(CGRectInset(rect, TEXT_MARGIN, TEXT_MARGIN), NULL);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
CFArrayRef lines = CTFrameGetLines(frame);
size_t numOfLines = CFArrayGetCount(lines);
CGPoint lineOrigins[numOfLines];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);
for (CFIndex i = 0; i < numOfLines; i++) {
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CFArrayRef runs = CTLineGetGlyphRuns(line);
size_t numOfRuns = CFArrayGetCount(runs);
for (CFIndex j = 0; j < numOfRuns; j++) {
CTRunRef run = CFArrayGetValueAtIndex(runs, j);
CGRect strFrame = CGRectZero;
CGFloat width = 0;
CGFloat height = 0;
CGFloat leading = 0;
CGFloat ascent = 0;
CGFloat descent = 0;
CFRange strRange = CTRunGetStringRange(run);
CGFloat offsetX = CTLineGetOffsetForStringIndex(line, strRange.location, NULL);
width = (CGFloat)CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
width += leading;
height = ascent + descent;
NSLog(#"Line %ld : Offset Y: %f", i+1, lineOrigins[i].y);
strFrame = CGRectMake(lineOrigins[i].x + offsetX + TEXT_MARGIN, lineOrigins[i].y + TEXT_MARGIN - descent, width, height);
strFrame = CGRectIntegral(strFrame);
CFDictionaryRef attr = CTRunGetAttributes(run);
if (attr != NULL) {
CFStringRef url = CFDictionaryGetValue(attr, kCTFontURLAttribute);
if (url != NULL) {
[self.wordsFrame addObject:[NSDictionary dictionaryWithObjectsAndKeys:(__bridge NSString *)url, #"URL", NSStringFromCGRect(strFrame), #"frame", nil]];
}
}
}
}
CTFrameDraw(frame, context);
CFRelease(font);
CGPathRelease(path);
CFRelease(frame);
CFRelease(framesetter);
Results:
2012-05-30 12:58:21.464 hjday[9362:707] Line 1 : Offset Y: 97.000000
2012-05-30 12:58:21.466 hjday[9362:707] Line 2 : Offset Y: 68.000000
2012-05-30 12:58:21.472 hjday[9362:707] Line 3 : Offset Y: 39.000000
Can anyone help me finger out what's wrong with my code? Or is there any other way(s) to get the origin of each CTRun?
I got it:
strFrame = CGRectMake(lineOrigins[i].x + offsetX + TEXT_MARGIN, self.bounds.size.height - (lineOrigins[i].y + TEXT_MARGIN - descent) - height, width, height);
need flip the y coordinate

How to render stretched text in iOS?

Given a rectangular area, I want to render some text using a specific font and have the rendered text fill out the rectangle. As in the image below:
This is not the same as just changing font size
Rendering it as a bitmap and then scale it is not an option (it looks horrible)
Vector graphics is the way to do it
Solution
I came up with the following which seems to work for my purposes. The code draws a single line of text scaling to fill the bounds. Subclass UIView and replace drawRect as follows.
- (void)drawRect:(CGRect)rect
{
[self drawScaledString:#"Abcde"];
}
- (void)drawScaledString:(NSString *)string
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
NSAttributedString *attrString = [self generateAttributedString:string];
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attrString, CFRangeMake(0, string.length),
kCTForegroundColorAttributeName, [UIColor redColor].CGColor);
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef) attrString);
// CTLineGetTypographicBounds doesn't give correct values,
// using GetImageBounds instead
CGRect imageBounds = CTLineGetImageBounds(line, context);
CGFloat width = imageBounds.size.width;
CGFloat height = imageBounds.size.height;
CGFloat padding = 0;
width += padding;
height += padding;
float sx = self.bounds.size.width / width;
float sy = self.bounds.size.height / height;
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 1, self.bounds.size.height);
CGContextScaleCTM(context, 1, -1);
CGContextScaleCTM(context, sx, sy);
CGContextSetTextPosition(context, -imageBounds.origin.x + padding/2, -imageBounds.origin.y + padding/2);
CTLineDraw(line, context);
CFRelease(line);
}
- (NSAttributedString *)generateAttributedString:(NSString *)string
{
CTFontRef helv = CTFontCreateWithName(CFSTR("Helvetica-Bold"),20, NULL);
CGColorRef color = [UIColor blackColor].CGColor;
NSDictionary *attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
(id)helv, (NSString *)kCTFontAttributeName,
color, (NSString *)kCTForegroundColorAttributeName,
nil];
NSAttributedString *attrString = [[[NSMutableAttributedString alloc]
initWithString:string
attributes:attributesDict] autorelease];
return attrString;
}
Example usage:
CGRect rect = CGRectMake(0, 0, 50, 280);
MyCTLabel *label = [[MyCTLabel alloc] initWithFrame:rect];
label.backgroundColor = [UIColor whiteColor];
[self addSubview:label];
You can set the UILabel transform property and scale the width:
[myLabel sizeToFit];
myLabel.transform = CGAffineTransformMakeScale(0.5, 1.0);
You could try CoreText. Get a CTFramesetter, calculate its rect, then calculate the affine transform necessary to compress that rect into the bounds you want and set that as the CTM. Then when you draw the text, it should stretch it appropriately at full quality.
you can also try with the UILabel's #property(nonatomic) BOOL adjustsFontSizeToFitWidth and #property minimumFontSize
Initially you can set the much higher value for font property and also initialize the minimumFontSize with a minimum font value.

show image in a CGContextRef

What i am doing, i downloded a code for calender now i want to show images on its tiles(for date).
What i am trying shows in code
- (void)drawTextInContext:(CGContextRef)ctx
{
CGContextSaveGState(ctx);
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
CGFloat numberFontSize = floorf(0.3f * width);
CGContextSetFillColorWithColor(ctx, kDarkCharcoalColor);
CGContextSetTextDrawingMode(ctx, kCGTextClip);
for (NSInteger i = 0; i < [self.text length]; i++) {
NSString *letter = [self.text substringWithRange:NSMakeRange(i, 1)];
CGSize letterSize = [letter sizeWithFont:[UIFont boldSystemFontOfSize:numberFontSize]];
CGContextSaveGState(ctx); // I will need to undo this clip after the letter's gradient has been drawn
[letter drawAtPoint:CGPointMake(4.0f+(letterSize.width*i), 0.0f) withFont:[UIFont boldSystemFontOfSize:numberFontSize]];
if ([self.date isToday]) {
CGContextSetFillColorWithColor(ctx, kWhiteColor);
CGContextFillRect(ctx, self.bounds);
} else {
// CGContextDrawLinearGradient(ctx, TextFillGradient, CGPointMake(0,0), CGPointMake(0, height/3), kCGGradientDrawsAfterEndLocation);
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename("left-arrow.png");
CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO, kCGRenderingIntentDefault);
//UIImage* image = [UIImage imageNamed:#"left-arrow.png"];
//CGImageRef imageRef = image.CGImage;
CGContextDrawImage(ctx, CGRectMake(8.0f+(letterSize.width*i), 0.0f, 5, 5), image);
//im.image=[UIImage imageNamed:#"left-arrow.png"];
}
CGContextRestoreGState(ctx); // get rid of the clip for the current letter
}
CGContextRestoreGState(ctx);
}
In else condition i want to show images on the tile so for that i am converting image objects in the CGImageRef.
Please help me.
I am not sure this would be done in same manner or in other manner please suggest your way to do this.
Thanx a lot.
The file-path of the image seems to problematic. You can retrieve the correct path with the NSBundle-methods. Also you're leaking a lot of memory, because you don't release your images and data-providers. To make a long story short, try this:
[[UIImage imageNamed:#"left-arrow.png"] drawInRect:...]
or even simpler:
[[UIImage imageNamed:#"left-arrow.png"] drawAtPoint:...]

How to change an UILabel/UIFont's letter spacing?

I've searched loads already and couldn't find an answer.
I have a normal UILabel, defined this way:
UILabel *totalColors = [[[UILabel alloc] initWithFrame:CGRectMake(5, 7, 120, 69)] autorelease];
totalColors.text = [NSString stringWithFormat:#"%d", total];
totalColors.font = [UIFont fontWithName:#"Arial-BoldMT" size:60];
totalColors.textColor = [UIColor colorWithRed:221/255.0 green:221/255.0 blue:221/255.0 alpha:1.0];
totalColors.backgroundColor = [UIColor clearColor];
[self addSubview:totalColors];
And I wanted the horizontal spacing between letters, to be tighter, whilst mantaining the font size.
Is there a way to do this? It should be a pretty basic thing to do.
Cheers guys,
Andre
UPDATE:
So I was forced to do it like this:
- (void) drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, "Arial-BoldMT", 60, kCGEncodingMacRoman);
CGContextSetCharacterSpacing (context, -10);
CGContextSetTextDrawingMode (context, kCGTextFill);
CGContextSetRGBFillColor(context, 221/255.0, 221/255.0, 221/255.0, 221/255.0);
CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
CGContextSetTextMatrix(context, xform);
char* result = malloc(17);
sprintf(result, "%d", totalNumber);
CGContextShowTextAtPoint (context, 0, 54, result, strlen(result));
}
But I need to align this to the right.
I could do that manually if I knew the width of the drawn text, but it's proving near impossible to find that.
I've read about ATSU, but I couldn't find any examples.
This sucks :/
From iOS 6 you can use NSAttributedString in UILabel.
In attributed string you can use attribute NSKernAttributeName to set letter spacing
NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString: #"Test test test test "];
[attrStr addAttribute:NSKernAttributeName value:#(4.0) range:NSMakeRange(0, attrStr.length)];
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, 300, 300, 100)];
label.attributedText = attrStr;
I've extended UILabel to change the character spacing. This should work out the box and pulls font, text, color etc from the UILabel itself (proper coding!).
You may notice I draw the text twice, first with clear color. This is to auto center the text in the label. Whilst this may be inefficient - isn't it nice to be auto centered?
Enjoy!
#interface RALabel : UILabel {
}
#end
#implementation RALabel
- (void) drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont (context, [self.font.fontName cStringUsingEncoding:NSASCIIStringEncoding], self.font.pointSize, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(context, 1);
CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
CGAffineTransform myTextTransform = CGAffineTransformScale(CGAffineTransformIdentity, 1.f, -1.f );
CGContextSetTextMatrix (context, myTextTransform);
// draw 1 but invisbly to get the string length.
CGPoint p =CGContextGetTextPosition(context);
float centeredY = (self.font.pointSize + (self.frame.size.height- self.font.pointSize)/2)-2;
CGContextShowTextAtPoint(context, 0, centeredY, [self.text cStringUsingEncoding:NSASCIIStringEncoding], [self.text length]);
CGPoint v =CGContextGetTextPosition(context);
// calculate width and draw second one.
float width = v.x - p.x;
float centeredX =(self.frame.size.width- width)/2;
CGContextSetFillColorWithColor(context, [self.textColor CGColor]);
CGContextShowTextAtPoint(context, centeredX, centeredY, [self.text cStringUsingEncoding:NSASCIIStringEncoding], [self.text length]);
}
I've come up with a solution for the letter spacing and the alignment to the right.
Here it goes:
NSString *number = [NSString stringWithFormat:#"%d", total];
int lastPos = 85;
NSUInteger i;
for (i = number.length; i > 0; i--)
{
NSRange range = {i-1,1};
NSString *n = [number substringWithRange:range];
UILabel *digit = [[[UILabel alloc] initWithFrame:CGRectMake(5, 10, 35, 50)] autorelease];
digit.text = n;
digit.font = [UIFont fontWithName:#"Arial-BoldMT" size:60];
digit.textColor = [UIColor colorWithRed:221/255.0 green:221/255.0 blue:221/255.0 alpha:1.0];
digit.backgroundColor = [UIColor clearColor];
[self addSubview:digit];
CGSize textSize = [[digit text] sizeWithFont:[digit font]];
CGFloat textWidth = textSize.width;
CGRect rect = digit.frame;
rect.origin.x = lastPos - textWidth;
digit.frame = rect;
lastPos = rect.origin.x + 10;
}
The letter spacing is the "10" on the last line.
The alignment comes from the lastPos.
Hope this helps anyone out there.
In Swift:
let myTitle = "my title"
let titleLabel = UILabel()
let attributes: NSDictionary = [
NSFontAttributeName:UIFont(name: "HelveticaNeue-Light", size: 20),
NSForegroundColorAttributeName:UIColor.whiteColor(),
NSKernAttributeName:CGFloat(2.0)
]
let attributedTitle = NSAttributedString(string: myTitle, attributes: attributes as? [String : AnyObject])
titleLabel.attributedText = attributedTitle
titleLabel.sizeToFit()
Not in any publicly available version of iPhone OS. ;-) If you are a current iPhone Developer, you can get an idea of where iPhone OS is going by looking through the "What's New" notes for iPhone OS 3.2.
Update: iOS v3.2, which added support for kerning, was still under NDA when I posted this. For an update-to-date answer, see How to set kerning in iPhone UILabel.