Friday, March 22, 2013

Playing with CoreText Framework(iOS 3.0+)

First of all i would like you to know the simple definition of CoreText, its a text engine found in iOS 3.2+ and OSX 10.5+ that gives you fine-grained control over text layout and formatting.

It sits in the sweet spot between UIKit and Core Graphics/Quartz:
In UIKit you have UILabel and you add a word or a text line on the screen by simple Drag-and-Drop in IB, but you cannot change the color of individual words.
In Core Graphics/Quartz you can do pretty much everything the system is capable of, but you need to calculate the position of each glyph in your text and draw it on the screen.
Core Text lies right between the two! You have complete control over position, layout, attributes like color and size, but Core Text takes care of everything else for you – from word wrap to font rendering and more.
Now lets start playing with it.

Just assume that you want to draw/create a new lable/textview based on the input text(font size and font name),the first thing which will pop-up from your mind is "How can we create a lable/textview based on the input text?".

Here is the answer,before drawing the text into lable/textview,you need to draw it on temporary paper
CGSize theSize = [urtext sizeWithFont:[UIFont systemFontOfSize:17.0f] constrainedToSize:CGSizeMake(310.0f, FLT_MAX) lineBreakMode:UILineBreakModeWordWrap];
Supply the default width of the lable/textview to be created.Now with theSize.height will get you the textview/lable to be created.

Now importantly what we will do is,we will add one more subview at the end of text/paragraph in lable/textview.Sounds crazy?.I too felt the same before doing it ;)

Here we are,hope you have gone through Apples NSAttributed feature :)
NSAttributedString* text = TextView.attributedText;//you can use lable too
CTFramesetterRef fs =
CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)text);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,self.aTextView.frame.size.width - 16,100000));  // why the -16? the frame should be the REAL TEXT FRAME, not the UITextView frame. If you look, there is a space between the view margin and the text. This is the baseline. Probably there is a method to calculate it programatically, but I can't check now. In my case it seems like 8px (*2)
CTFrameRef f = CTFramesetterCreateFrame(fs, CFRangeMake(0, 0), path, NULL);
CTFrameDraw(f, NULL);
NSRange lastRange;
NSArray* lines = (__bridge NSArray*)CTFrameGetLines(f);
id lastLineInArray = [lines lastObject];
CTLineRef theLine = (__bridge CTLineRef)lastLineInArray;
CFRange range = CTLineGetStringRange(theLine);
lastRange.length = range.length;
lastRange.location = range.location - 1;
NSLog(@"%ld %ld", range.location, range.length);
// this is the last line
NSString *lastLine = [aTextView.text substringWithRange:lastRange];
CGSize sizeOfString = [lastLine sizeWithFont:self.aTextView.font constrainedToSize:aTextView.frame.size;

you will obtain the width and the height of the string, and then the final position of your button (for the y position: number of lines taken from the lines array count * string height)
Now pass the correct rect to the UITextView/UILable and you're DONE!.

Happy Coding :-)...

1 comment:

  1. Hi there Dear, are you actually visiting this site regularly, if so afterward you will absolutely obtain pleasant knowledge.

    my blog post :: book of rah