Is it possible to set different font in One UIlabel? - iphone

I have a string like "This is a good apple." to display on my UIlabel. I would like to set the word "good" with different font. Just look like "This is a good apple."

Take a look at NSAttributedString... Use OHAttributedLabel draw NSAttributedString because UILabel,UITextView doesnot support NSAttributedString...
PS: if you plan to distribute an iOS6 or newer application, as UILabel now support NSAttributedString, you should use UILabel directly instead of OHAttributedLabel as it is now natively supported by the OS.

There is a way to set different / multiple fonts & other properties on Label using NSMutableAttributedString.
Foll is my code:
UIFont *ArialFont = [UIFont fontWithName:#"arial" size:18.0];
NSDictionary *arialdict = [NSDictionary dictionaryWithObject: ArialFont forKey:NSFontAttributeName];
NSMutableAttributedString *AattrString = [[NSMutableAttributedString alloc] initWithString:title attributes: arialdict];
UIFont *VerdanaFont = [UIFont fontWithName:#"verdana" size:12.0];
NSDictionary *veradnadict = [NSDictionary dictionaryWithObject:VerdanaFont forKey:NSFontAttributeName];
NSMutableAttributedString *VattrString = [[NSMutableAttributedString alloc]initWithString: newsDate attributes:veradnadict];
[VattrString addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:(NSMakeRange(0, 15))];
[AattrString appendAttributedString:VattrString];
lblText.attributedText = AattrString;
Note that lblText is the UILabel, outlet as file owner.
One can keep on appending as many NSMutableAttributedString he wants..
Also Note that I've added verdana & arial font in my project & added a plist for the same.

This is not possible with UILabel. But you could use Core Text.
An other easier approach could be to use a very small UIWebView. In this you can set the font using html commands.

Im sorry but that's not possible with the UILabel implementation, you have two options:
You can create a UIWebView and put the text HTML formatted
You can make your own UILabel implementation with that functionallity, with, for example, Core Text

Instead of using two UILabels, you can have one label with multiple text fonts styles and colors. Here is an example :
UILabel *customLbl = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 25)];
customLbl.backgroundColor = [UIColor yellowColor];
[self createTwoTextStyleInSingleUILabel:customLbl];// custom method calling
[self.view addSubview:customLbl];
Custom Method goes like this :
Before applying this method, add QuartzCore framework (needed for CALayers), and CoreText framework(needed for the attributed string.) in your project.
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
- (void)createTwoTextStyleInSingleUILabel: (UILabel *) myLabel{
NSString *firstText = NSLocalizedString(#"First text:", nil);
NSString *secondText = [NSString stringWithFormat:#"%# %#",firstText,#"Second text"];
CATextLayer *myLabelTextLayer;
/* Create the text layer on demand */
if (!myLabelTextLayer) {
myLabelTextLayer = [[CATextLayer alloc] init];
myLabelTextLayer.backgroundColor = [UIColor clearColor].CGColor;
myLabelTextLayer.wrapped = NO;
CALayer *layer = myLabel.layer; //assign layer to your UILabel
myLabelTextLayer.frame = CGRectMake((layer.bounds.size.width-180)/2 + 10, (layer.bounds.size.height-30)/2 + 10, 180, 30);
myLabelTextLayer.contentsScale = [[UIScreen mainScreen] scale];
myLabelTextLayer.alignmentMode = kCAAlignmentCenter;
[layer addSublayer:myLabelTextLayer];
}
/* Create the attributes (for the attributed string) */
// customizing first string
CGFloat fontSize = 16;
UIFont *boldFont = [UIFont boldSystemFontOfSize:fontSize];
CTFontRef ctBoldFont = CTFontCreateWithName((__bridge CFStringRef)boldFont.fontName, boldFont.pointSize, NULL);
CGColorRef cgColor = [UIColor redColor].CGColor;
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)ctBoldFont, (id)kCTFontAttributeName,
cgColor, (id)kCTForegroundColorAttributeName, nil];
CFRelease(ctBoldFont);
// customizing second string
UIFont *font = [UIFont systemFontOfSize:16];
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL);
CGColorRef cgSubColor = [UIColor blackColor].CGColor;
NSDictionary *subAttributes = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)ctFont, (id)kCTFontAttributeName,cgSubColor, (id)kCTForegroundColorAttributeName, nil];
CFRelease(ctFont);
/* Create the attributed string (text + attributes) */
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:secondText attributes:attributes];
float lengthOfSecondString = 12.0; // length of second string including blank space inbetween text, space in front , space after text.. Be careful, your app may crash here if length is beyond the second text length (lengthOfSecondString = text length + blank spaces)
[attrStr addAttributes:subAttributes range:NSMakeRange(firstText.length, lengthOfSecondString)];
// you can add another subattribute in the similar way as above , if you want change the third textstring style
/* Set the attributes string in the text layer :) */
myLabelTextLayer.string = attrStr;
myLabelTextLayer.opacity = 1.0; //to remove blurr effect
}

Modified Akshay's answer and implemented it with Swift:
// First part with font size 10
let firstPartInfo = [NSFontAttributeName: UIFont.systemFontOfSize(10.0)]
var attributedString = NSMutableAttributedString(string: "First Part", attributes: firstPartInfo)
// Second part with font size 20
let secondPartInfo = [NSFontAttributeName: UIFont.systemFontOfSize(20.0)]
attributedString.appendAttributedString(
NSAttributedString(string: "Second Part", attributes: secondPartInfo))
// Lastly set the attributed text
label.attributedText = attributedString
No reason to use third party solutions or subclasses.

Related

Overriding UILabel

I have to present the currency and the price in a UILabel. In one label, but using different font sizes. Now it looks like this:
... and I did it overriding drawTextInRect: like this:
- (void)drawTextInRect:(CGRect)rect{
CGSize textSize = [self.text sizeWithFont:self.font];
textSize.width -= textSize.width/12;
CGRect analogRect = CGRectMake(0, 0, textSize.width, self.frame.size.height);
CGPoint centrino = self.center;
self.frame = analogRect;
self.center = centrino;
NSString *currency = [self.text substringWithRange:NSMakeRange(0, 3)];
NSString *amount = [self.text substringWithRange:NSMakeRange(3, self.text.length - 3)];
self.text = currency;
self.textAlignment = UITextAlignmentLeft;
self.font = [UIFont fontWithName:#"Helvetica-Bold" size:30.];
self.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
[super drawTextInRect:analogRect];
self.text = amount;
self.textAlignment = UITextAlignmentRight;
self.font = [UIFont fontWithName:#"Helvetica-Bold" size:40.];
[super drawTextInRect:analogRect];
}
Nice, isn't it?
But I need to align the currency and the price at the bottom, like this:
As you can see, I need to force "EUR" to go lower, because its size is smaller and it's centered, so it looks higher.
Please suggest a way to do this.
Thanks in advance!
NOTE:
Using 2 different labels is not good for me. I gotta do it in a single label.
Two ideas: 1) Since you're subclassing UILabel, you could put a second label for the currency type within the subclass' implementation, and your view would still think there's only a single label. 2) Since the NDA is lifted today on iOS 6, I'll suggest taking a look at attributed strings. Oh, and +1 for writing 'a UILabel' and not 'an UILabel.'
Ok, guys, thanks to everybody for your fast answers, but I have no time for researches, so I have found a newbie solution.
I feel shame but here is it:
- (void)setCurrencyAndPrice{
//set the labels' texts
[label_currency setText:#"EUR"];
[label_amount setText:#"12.34"];
//set the sizes to fit the content
[label_currency sizeToFit];
[label_amount sizeToFit];
//read the new frame of the labels
CGRect curRect = label_currency.frame;
CGRect amoRect = label_amount.frame;
//adjust the position of the price to the top-right
[label_amount setFrame:CGRectMake(320 - amoRect.size.width - 10, 0, amoRect.size.width, amoRect.size.height)];
//read again the price frame
amoRect = label_amount.frame;
//stick the currency to the price, spacing 10 pixels
[label_currency setFrame:CGRectMake(amoRect.origin.x - curRect.size.width - 10, 11, curRect.size.width, curRect.size.height)];
}
As you can see, nothing to override, just using 2 different labels - exactly what I did NOT want to do, but the time runs faster that I am coding.
Cheers!
P.S.: Although I have found the solution for myself, I like #MichaelMangold' idea, so I accept his answer.
Instead of using two UILabels, you can have one label with multiple text fonts styles and colors. Here is an example, may be that might help you :
UILabel *customLbl = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 25)];
customLbl.backgroundColor = [UIColor yellowColor];
[self createTwoTextStyleInSingleUILabel:customLbl];// custom method calling
[self.view addSubview:customLbl];
Custom Method goes like this :
Before applying this method, add QuartzCore framework (needed for CALayers), and CoreText framework(needed for the attributed string.) in your project.
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
- (void)createTwoTextStyleInSingleUILabel: (UILabel *) myLabel{
NSString *firstText = NSLocalizedString(#"First text:", nil);
NSString *secondText = [NSString stringWithFormat:#"%# %#",firstText,#"Second text"];
CATextLayer *myLabelTextLayer;
/* Create the text layer on demand */
if (!myLabelTextLayer) {
myLabelTextLayer = [[CATextLayer alloc] init];
myLabelTextLayer.backgroundColor = [UIColor clearColor].CGColor;
myLabelTextLayer.wrapped = NO;
CALayer *layer = myLabel.layer; //assign layer to your UILabel
myLabelTextLayer.frame = CGRectMake((layer.bounds.size.width-180)/2 + 10, (layer.bounds.size.height-30)/2 + 10, 180, 30);
myLabelTextLayer.contentsScale = [[UIScreen mainScreen] scale];
myLabelTextLayer.alignmentMode = kCAAlignmentCenter;
[layer addSublayer:myLabelTextLayer];
}
/* Create the attributes (for the attributed string) */
// customizing first string
CGFloat fontSize = 16;
UIFont *boldFont = [UIFont boldSystemFontOfSize:fontSize];
CTFontRef ctBoldFont = CTFontCreateWithName((__bridge CFStringRef)boldFont.fontName, boldFont.pointSize, NULL);
CGColorRef cgColor = [UIColor redColor].CGColor;
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)ctBoldFont, (id)kCTFontAttributeName,
cgColor, (id)kCTForegroundColorAttributeName, nil];
CFRelease(ctBoldFont);
// customizing second string
UIFont *font = [UIFont systemFontOfSize:16];
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL);
CGColorRef cgSubColor = [UIColor blackColor].CGColor;
NSDictionary *subAttributes = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)ctFont, (id)kCTFontAttributeName,cgSubColor, (id)kCTForegroundColorAttributeName, nil];
CFRelease(ctFont);
/* Create the attributed string (text + attributes) */
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:secondText attributes:attributes];
float lengthOfSecondString = 12.0; // length of second string including blank space inbetween text, space in front , space after text.. Be careful, your app may crash here if length is beyond the second text length (lengthOfSecondString = text length + blank spaces)
[attrStr addAttributes:subAttributes range:NSMakeRange(firstText.length, lengthOfSecondString)];
// you can add another subattribute in the similar way as above , if you want change the third textstring style
/* Set the attributes string in the text layer :) */
myLabelTextLayer.string = attrStr;
myLabelTextLayer.opacity = 1.0; //to remove blurr effect
}
This library will work for you https://github.com/AliSoftware/OHAttributedLabel
I'd build a UIView subclass called ValueUnitsLabel. Give it two labels and let it control framing, font sizes etc. It can also be smart about reading NSLocale.

UILabel default kerning different from CATextLayer

I have a UILabel with the string 'LA'. I also have a CATextLayer with the same characters in an NSAttributedString assigned to its string property. The kerning in the UILabel is noticeably different from the CATextLayer. Here's the code.
- (void)viewDidLoad
{
[super viewDidLoad];
//
// UILabel
//
UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, 280, 100)];
label1.text = #"LA";
label1.backgroundColor = [UIColor clearColor];
label1.font = [UIFont fontWithName:#"Futura" size:90.0];
[self.view addSubview:label1];
//
// CATextLayer
//
UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(20, 130, 280, 100)];
label2.backgroundColor = [UIColor clearColor];
CATextLayer *textLayer = [[CATextLayer alloc] init];
textLayer.frame = label2.layer.bounds;
textLayer.contentsScale = [[UIScreen mainScreen] scale];
[label2.layer addSublayer:textLayer];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"LA"];
CTFontRef aFont = CTFontCreateWithName((__bridge CFStringRef)#"Futura", 90.0, NULL);
[string addAttribute:(NSString*)kCTFontAttributeName value:(__bridge id)aFont range:NSMakeRange(0, [string length])];
textLayer.string = string;
[self.view addSubview:label2];
}
Here's an image of the results.
Why is the kerning different between these two methods and what am I doing wrong in the CATextLayer example?
UIKit generally uses WebKit for its text rendering (as visible in this crash log), most likely for performance reasons. If you really need super-precision then there are some custom UILabel reimplementations using CoreText as its back-end.
EDIT:
As of iOS7 this is no longer true since UILabel uses TextKit for its rendering which is based on CoreText as well.
you should add attribute to your NSMutableAttributedString.
For the kerning:
CGFloat characterspacing = 10.0f;
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&characterspacing);
[string addAttribute:(id)kCTKernAttributeName value:(id)num range:NSMakeRange(0 , [string length])];
CFRelease(num);
If you also need the line spacing, or set LineBreadMode:
CTLineBreakMode linebreak = kCTLineBreakByCharWrapping;
CTParagraphStyleSetting linebreakStyle;
linebreakStyle.spec = kCTParagraphStyleSpecifierLineBreakMode;
linebreakStyle.valueSize = sizeof(linebreak);
linebreakStyle.value = &linebreak;
CTParagraphStyleSetting lineSpaceStyle;
CGFloat linespacing = self.linesSpacing;
lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;
lineSpaceStyle.valueSize = sizeof(linespacing);
lineSpaceStyle.value =&linespacing;
CTParagraphStyleSetting settings[ ] ={linebreakStyle,lineSpaceStyle};
CTParagraphStyleRef style = CTParagraphStyleCreate(settings ,2);
[string addAttribute:(id)kCTParagraphStyleAttributeName value:(id)style range:NSMakeRange(0 , [string length])];
CFRelease(style);
At the end, may you need calculate the number of line(linenum) about your kerning,line spacing and LineBreakMode:
CTFramesetterRef myframesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
CGMutablePathRef leftColumnPath = CGPathCreateMutable();
CGPathAddRect(leftColumnPath, NULL ,CGRectMake(0 , 0 , Lable.frame.size.width, MAXFLOAT));
CTFrameRef leftFrame = CTFramesetterCreateFrame(myframesetter,CFRangeMake(0, 0), leftColumnPath , NULL);
CFArrayRef lines = CTFrameGetLines(leftFrame);
linenum = (int)CFArrayGetCount(lines);
CFRelease(myframesetter);
CFRelease(leftFrame);
CGPathRelease(leftColumnPath);
Well Core Text is really different when compared to drawing strings using UIKit, probably because it comes from Core Foundation and not AppKit or UIKit. I do understand your requirements to use a label for doing the metrics hard job on a string. The only solution for me is to match the kerning of UILabel in the attributed string, unfortunately I don't know the exact value but you can use this property to change that value kCTKernAttributeName. You should pay attention also for the interline that could be not the same.
Forcing that value to the matching kerning you could have the correct behavior. If you want the opposite (match CT kerning) you should do some math an later apply to the label a UIEdgeInset to math the correct label.
Hope this helps.

CoreText Horizontal Center Alignment of Text

I'm using CoreText and I want to make it so my NSMutableAttributedString always center aligns its text. This is the code I'm using and it isn't working:
CTTextAlignment paragraphAlignment = kCTCenterTextAlignment;
CTParagraphStyleSetting paragraphSettings[1] = {{kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &paragraphAlignment}};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphSettings, 1);
text = [[NSMutableAttributedString alloc] initWithString:#"" attributes:[NSDictionary dictionaryWithObjectsAndKeys: (id)paragraphStyle, (NSString*)kCTParagraphStyleAttributeName, nil]];
Does anyone have any idea how to do this correctly?
Other solution without to use CoreText if your app is compatible with 10.10 or upper, it is to use NSParagraphStyle.
NSMutableParagraphStyle *rectStyle = NSMutableParagraphStyle.defaultParagraphStyle.mutableCopy;
rectStyle.lineBreakMode = NSLineBreakByClipping;
rectStyle.alignment = NSTextAlignmentCenter;
NSMutableAttributedString *att = [[NSMutableAttributedString alloc] initWithString:myText attributes:#{NSParagraphStyleAttributeName : rectStyle}];

How to break a string into two parts

I am parsing a string from my plist and trying to break it into two parts to display them in two or three lines as per the break.
NSArray * parts = [text componentsSeparatedByString:#"\n"]; // this text is coming from plist.
int nthLine = 0;
for(NSString *str in parts)
{
CGRect outerFrame = CGRectMake(frame1.origin.x, frame1.origin.y + 45*nthLine, frame1.size.width, 45);
SFNDoorStyledView * question = [[SFNDoorStyledView alloc]initWithFrame:outerFrame];
question.backgroundColor = [UIColor clearColor];
question.tag = 2;
[question drawString:text inRect:CGRectMake(0,0,500,150) usingFontNamed:#"Futura-Bold" size:40.0 lineSpacing:40.0 kernValue:-3.0 color:#"#7d7066"];
[self.view addSubview:question];
}
nthLine = nthLine +1;
Be careful what \n means in different contexts.
In this context
NSArray * parts = [text componentsSeparatedByString:#"\n"];
\n is interpreted as a newline character.
In your plist \n will be the actual characters \ and n
You could use option-enter to add the newline character to the plist and then use your code above or better yet:
NSArray * parts = [text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
Or if you want to use this custom '\n' delimiter you could use
NSArray * parts = [text componentsSeparatedByString:#"\\n"];
Consider using a UILabel to hold your text, which you can size dynamically based on the string itself. No need to include \n in the string. Just size/constrain your label to the width you need, and the height will be calculated for you based on other attributes like font and font size. Here's a representative snippet.
// Size the label based on the font and actual text
UILabel *l = [[[UILabel alloc] initWithFrame:CGRectMake(10, 5, 300, 50)] autorelease];
l.font = [UIFont boldSystemFontOfSize:14.0];
l.lineBreakMode = UILineBreakModeWordWrap;
l.textAlignment = UITextAlignmentCenter;
l.numberOfLines = 0; // Allow for as many lines as needed
l.backgroundColor = [UIColor clearColor];
CGSize constraintSize = CGSizeMake(300, MAXFLOAT);
CGSize labelSize = [l.text sizeWithFont:l.font
constrainedToSize:constraintSize
lineBreakMode:UILineBreakModeWordWrap];
CGRect frame = l.frame;
frame.size = labelSize;
// Center it
frame.origin.x = floor((320.0 - labelSize.width) / 2.0);
l.frame = frame;
[v addSubview:l];
Check if the line separator is actually \n. It could also be \r. So using componentsSeparatedByString:#"\r\n" might help.
Actually plist saves your string as a text. SO if you enter \n it takes it as a text and not as linebreak. In spite of doing it i break the string in plist only. For ex: if you have a string , What is your current income monthly? and you want to break it from income. Then you can write What is your and then press alt+enter and enter the text in new line.

UILabel not aligning perfectly center when wrapping

See image for example: http://img25.imageshack.us/img25/6996/90754687.png
The grey background indicates the size of the UILabel frame.
For some reason, the first line of wrapped text doesn't seem to always center, even though I'm using UITextAlignmentCenter.
Here's the code I use to set up my labels:
self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
titleLabel.font = [UIFont boldSystemFontOfSize:fontHeight];
titleLabel.backgroundColor = [UIColor grayColor];
titleLabel.numberOfLines = 2;
titleLabel.lineBreakMode = UILineBreakModeMiddleTruncation;
NSString * title = file.name;
CGSize maximumSize = CGSizeMake(thumbnailWidth+4,fontHeight * 3);
UIFont * font = [UIFont boldSystemFontOfSize:12];
CGSize stringSize = [title sizeWithFont:font constrainedToSize:maximumSize lineBreakMode:titleLabel.lineBreakMode];
CGRect stringFrame = CGRectMake(0, thumbnailHeight + thumbnailPadding, thumbnailWidth + 4, stringSize.height);
titleLabel.text = title;
titleLabel.frame = stringFrame;
titleLabel.textAlignment = UITextAlignmentCenter;
Is that because there are no spaces in the text? In IB it appears to react just like your getting, if you have no spaces. Setting the line break mode to character wrap tends to center the second lines to the first, but that may not be entirely what you want either.