Monday, 5 March 2012

Label to display NSAttributedString (Label through a Path)

Hi All,

I don't know how many of you are familiar with NSAttributedString, if we simply write a definition "Attributed String is a string that itself contains its properties to display (like color, thickness, font, style all that are need to display the string)".

So the Attributed String help the programmer to display a long string, as different paragraph (with line breaks, justification etc) with different fonts and color for different part of string.
To achieve this iOS provide a framework called "CoreText" which is made available from Xcode 3.2 onwards. But in iOS as of now we have only limited access for this framework resource because Mac framework had lot of method to read and write Attributed String from files(like doc, docx etc).

While discussing the limitation of Attributed String processing in iOS the important think we can find is there is no UI (User Interface) elements to display Attributed String. The CoreText framework provide a method to draw the string in a path, which is not as simple like a string displayed in UILabel's.

So I got up with an implementation of a custom class that will draw the given Attributed String with a specified path.

See the screen Shots,

//To Display the NSAttributedString with NSAttributedLabel,
(Here prefix NS means not 'Next Step' it just 'Naveen Shan' my name, if you don't like change it.)

1. Create a object of NSAttributedLabel set Frame and Attributed String and addSubview the label where you need to show (here is the simplicity now this class acts like UILabel to display String).

Things to be noted is,
* We need to set enough attribute to the string before we addSubview the label. It is because, as we all know the drawRect method of a view will call at the time of setting superview for that view, if we set the attributes later we need to call a setNeedsDisplay method for proper functioning.
* We need to set Background color for that view. It is because I override the drawRect method of the view so if we not set the default background color s black.

 -(void)showAttributedLabel {  
   NSString *string = @"Lorem ipsum \n dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";  
   //normal font attributes  
   CTFontRef normalFontRef = CTFontCreateWithName((CFStringRef)@"Helvetica", 18, NULL);  
   NSDictionary* normalFontAttribute = [[NSDictionary alloc] initWithObjectsAndKeys:(__bridge id)normalFontRef,(NSString*)kCTFontAttributeName, nil];  
   //bold font attributes  
   CTFontRef boldFontRef = CTFontCreateWithName((CFStringRef)@"Helvetica-Bold", 20, NULL);  
   NSDictionary* boldFontAttribute = [[NSDictionary alloc] initWithObjectsAndKeys:(__bridge id)boldFontRef,(NSString*)kCTFontAttributeName, nil];  
   NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];  
   //setting attributes.  
   [attributedString addAttributes:normalFontAttribute range:NSMakeRange(0, [attributedString length])];  
   [attributedString addAttributes:boldFontAttribute range:NSMakeRange(0, 11)];  
   NSAttributedLabel *objAttributedLabel = [[NSAttributedLabel alloc] init];  
   [objAttributedLabel setFrame:CGRectMake(50, 50, (self.view.frame.size.width/2), 500)];  
   [objAttributedLabel setBackgroundColor:[UIColor clearColor]];  
   [objAttributedLabel setAttributedString:attributedString];  
   [self.view addSubview:objAttributedLabel];  
   [objAttributedLabel release];  
   objAttributedLabel = nil;  
   string = nil;  
   [boldFontAttribute release];  
   boldFontAttribute = nil;  
   [normalFontAttribute release];  
   normalFontAttribute = nil;  

Some More Interesting Abilities of NSAttributedLabel,
Here I provide a flexibility of displaying string in columns(in vertical span).

First I like to tell how the NSAttributedLabel can be used just as a Label to display Attributed String
Set the KCOLUMNCOUNT in NSAttributedLabel.h as 0 .

if you need more than one column you need to set the required column count in KCOLUMNCOUNT at NSAttributedLabel.h
and specify the rect for each column in '-(CGRect)rectForColumnAtIndex:(int)index' method at NSAttributedLabel.m

One Important Tip
The Attributed String is drawn from its end point and from end of the path to start of the path. so if the specified path rect is not enough to show the string then the top (start) of the string is missing to draw (not the end). Similarly if you try to put some extra space at the bottom of path to hold lengthy string, I think its not possible.
To Sole this problem I can only be able to provide a function that will return the length of string that the given frame can accumulate, here it is,

 - (int)endIndexOfAttributtedString:(NSMutableAttributedString *)attributtedString InFrame:(CGRect)rect {  
   int currentIndex = 0; //now I map it from string beginning.  
   if (attributtedString != nil)  {  
     CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributtedString);  
     CGMutablePathRef path = CGPathCreateMutable();  
     CGPathAddRect(path, NULL, rect);  
     CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(currentIndex, 0), path, NULL);  
     CFRange frameRange = CTFrameGetVisibleStringRange(frame);  
     currentIndex += frameRange.length;  
   return currentIndex;  

I attached Complete code here

if you need more details feel free to contact me

Naveen Shan

No comments:

Post a Comment