UILabel - attributedText - setLineHeightMultiple - ios

i´m using a UILabel to show a text.
The problem is, that i have different line spaces between different IOS versions. IOS7.1 is less space than IOS8.1
What the best way to fix it? I don´t have all simulators installed, so i can´t figure out, when the problem appears. Does anyone have a solution? This is the current code:
NSString *startGameBasic_title = #"TRACK QUIZ"; // FONT SIZE 20 px
NSString *startGameBasic_1 = #"BASIC"; // FONT SIZE 76 px
NSString *startGameBasic_2 = #"MODE"; // FONT SIZE 76 px
NSString *startGameBasic_textRe = [NSString stringWithFormat:#"%#\n\n%#\n%#",
startGameBasic_title,
startGameBasic_1,
startGameBasic_2];
// Define general attributes like color and fonts for the entire text
NSDictionary *startGameBasic_attribsRe = #{
NSForegroundColorAttributeName: GENERAL_BACK_BUTTON_COLOR,
NSFontAttributeName:START_VIEW_FONT_SMALL,
NSKernAttributeName:#(0.0f)
};
NSMutableAttributedString *startGameBasic_attributedTextRe = [[NSMutableAttributedString alloc] initWithString:startGameBasic_textRe attributes:startGameBasic_attribsRe];
//TITEL
NSRange startGameBasic_text_title = [startGameBasic_textRe rangeOfString:startGameBasic_title];
[startGameBasic_attributedTextRe setAttributes:#{NSForegroundColorAttributeName:GENERAL_BACK_BUTTON_COLOR,
NSFontAttributeName:START_VIEW_FONT_SMALL,
NSKernAttributeName:#(-0.5f)}
range:startGameBasic_text_title];
//FIRSTROW
NSRange startGameBasic_text_1 = [startGameBasic_textRe rangeOfString:startGameBasic_1];
[startGameBasic_attributedTextRe setAttributes:#{NSForegroundColorAttributeName:GENERAL_BACK_BUTTON_COLOR,
NSFontAttributeName:START_BASIC_BUTTON,
NSKernAttributeName:#(-3.5f)}
range:startGameBasic_text_1];
//SECONDROW
NSRange buttonReStart_text_2 = [startGameBasic_textRe rangeOfString:startGameBasic_2];
[startGameBasic_attributedTextRe setAttributes:#{NSForegroundColorAttributeName:GENERAL_BACK_BUTTON_COLOR,
NSFontAttributeName:START_BASIC_BUTTON,
NSKernAttributeName:#(-2.8f)}
range:buttonReStart_text_2];
// Create NSMutableParagraphStyle object
NSMutableParagraphStyle *startGameBasic_paragraphRe = [[NSMutableParagraphStyle alloc] init];
startGameBasic_paragraphRe.alignment = NSTextAlignmentCenter;
[startGameBasic_paragraphRe setLineHeightMultiple:0.65];
[startGameBasic_attributedTextRe addAttribute:NSParagraphStyleAttributeName value:startGameBasic_paragraphRe range:NSMakeRange(0, [startGameBasic_attributedTextRe length])];
startGameBasic_txt.attributedText = startGameBasic_attributedTextRe;
Right now, i have the idea to do it like this, but maybe there is much better way:
NSMutableParagraphStyle *startGameBasic_paragraphRe = [[NSMutableParagraphStyle alloc] init];
startGameBasic_paragraphRe.alignment = NSTextAlignmentCenter;
float SystemVersion = [[UIDevice currentDevice].systemVersion floatValue];
if (SystemVersion < 8.0)
{
[startGameBasic_paragraphRe setLineHeightMultiple:0.85];
} else {
[startGameBasic_paragraphRe setLineHeightMultiple:0.65];
}
I think to use the UITextView is not a solution, as i can´t set "scale font size". Would be great to get your help.

Related

Wrong bounding rect for right aligned text in UILabel

- (CGRect)boundingRectForGlyphRange:(NSRange)glyphRange inTextContainer:(NSTextContainer *)container;
returns wrong rect for glyph range when attributed text is right aligned in UILabel. How to fix this please ?
Code to calculate the bounding rect of attributed text when tapped on UILabel (textAlignment set to right in UILabel)
- (void)tap:(UITapGestureRecognizer *)recognizer {
UILabel *label = (UILabel *)recognizer.view;
CGSize labelSize = recognizer.view.bounds.size;
NSTextContainer *container = [[NSTextContainer alloc] initWithSize:labelSize];
container.lineFragmentPadding = 0.0;
container.lineBreakMode = label.lineBreakMode;
container.maximumNumberOfLines = label.numberOfLines;
NSLayoutManager *manager = [NSLayoutManager new];
[manager addTextContainer:container];
NSTextStorage *storage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
[storage addLayoutManager:manager];
CGPoint touchPoint = [recognizer locationInView:label];
NSInteger indexOfCharacter = [manager characterIndexForPoint:touchPoint
inTextContainer:container
fractionOfDistanceBetweenInsertionPoints:nil];
NSRange range = NSMakeRange(indexOfCharacter, 1);
NSRange glyphRange;
[manager characterRangeForGlyphRange:range actualGlyphRange:&glyphRange];
CGRect rect = [manager boundingRectForGlyphRange:glyphRange inTextContainer:container];
}
The problem is that you are setting the label's text and textAlignment, and then pulling out the label's attributedText and handing it to the text kit stack, in the belief that it magically translates your label configuration into an attributed string. It doesn't!
If you want right-aligned text that the text kit stack can see as right-aligned, set your label's attributedText only, endowing it with features such as a right-aligned paragraph style.
Solved this!!
The problem was the label was made to be right aligned using textAlignment property of UILabel. As mentioned in comments label.attributedText doesn't know anything about it.
Instead add text alignment using NSParagraphStyleAttributeName as :
NSMutableAttributedString *mutableString = [NSMutableAttributedString new];
//Add to your string
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setAlignment:NSTextAlignmentRight];
[mutableString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, mutableString.length)];
label.attributedText = mutableString;

Problems with sizes and lines in UILabel

I have a little question. I developed an application that automatically fills a data set obtained from a SQLite database. These data are drawn from dynamic way, and between the data I have a frame where I insert labels dynamically. I never know the exact number of labels inside these frame because I take data from different tables of my Database. The problem I have with the labels which texts within them do not fill within the width of the label. I tried to use label.linebreakmode but still makes the break. I post the code:
There I have many objects taken from previous code, like widthFormato and widthImageFormato
if([tipoVino length]!=0){
UILabel *lblFormato = [[UILabel alloc] init];
labelX = ((widthFormato-widthImageFormatos) / 2)+10;
CGRect labelFrame = lblFormato.frame;
labelFrame.size.width = widthImageFormatos;
labelFrame.origin.x = labelX;
labelFrame.origin.y = labelY+25;
lblFormato.frame = labelFrame;
lblFormato.numberOfLines = 0;
lblFormato.lineBreakMode = NSLineBreakByWordWrapping;
[lblFormato setText:[NSString stringWithFormat:#"- %#",tipoVino]];
lblFormato.textColor = [UIColor whiteColor];
labelY = lblFormato.frame.origin.y;
[formatosImageView addSubview:lblFormato];
}
I think that you want to create labels with flexible height (not all have the same size), and must fix the width of formatosImageView.
EDITED FOR iOS7
I was using sizeWithFont that is deprecated in iOS7, I changed it for boundingRectWithSize
Try this:
UILabel *lblFormato = [[UILabel alloc] init];
lblFormato.numberOfLines = 0;
lblFormato.lineBreakMode = NSLineBreakByWordWrapping;
[lblFormato sizeToFit];
NSString *string = [NSString stringWithFormat:#"- %#", tipoVino];
[lblFormato setText: string];
//Calculate the size of the container of the lblFormato
CGSize size = [string boundingRectWithSize:CGSizeMake(widthImageFormatos, 2000) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{NSFontAttributeName: [UIFont fontWithName:#"customFont" size:fontSize]} context:nil];
lblFormato.frame = CGRectMake(labelX, labelY + 25, size.width, size.height);
and you must update the labelY with:
labelY = lblFormato.frame.size.height;
Maybe it helps you.
With regards #spinillos answer, for iOS 7:
NSDictionary *attributes = #{ NSFontAttributeName : [UIFont systemFontOfSize:<#fontSize#>] };
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
initWithString: <#string#>,
attributes:attributes];
CGRect frame = [attributedString boundingRectWithSize:CGSizeMake(<#width#>, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil];

drawInRect:withAttributes vs drawInRect:withFont:lineBreakMode:alignment

I'm working on a new version of my app and am attempting to replace deprecated messages, but am not able to get past this one.
I can't figure out why drawInRect:withAttributes is not working. The code displays properly when drawInRect:withFont:lineBreakMode:alignment message is sent, but does not work when drawInRect:withAttributes is sent.
I'm using the same rect and font and I what I believe is the same text style. The constants are just positioning the rect just below an image, but I'm using the same rect for both calls, so I'm certain the rectangle is correct.
(note that bs.name used below is an NSString object)
CGRect textRect = CGRectMake(fCol*kRVCiPadAlbumColumnWidth,
kRVCiPadAlbumColumnWidth-kRVCiPadTextLabelYOffset,
kRVCiPadAlbumColumnWidth,
kRVCiPadTextLabelHeight);
NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
textStyle.lineBreakMode = NSLineBreakByWordWrapping;
textStyle.alignment = NSTextAlignmentCenter;
UIFont *textFont = [UIFont systemFontOfSize:16];
This doesn't work (nothing is drawn on the screen) using the variables from above
[bs.name drawInRect:textRect
withAttributes:#{NSFontAttributeName:textFont,
NSParagraphStyleAttributeName:textStyle}];
This Does work (the string is drawn properly on the screen) using the same variables from above
[bs.name drawInRect:textRect
withFont:textFont
lineBreakMode:NSLineBreakByWordWrapping
alignment:NSTextAlignmentCenter];
Any assistance would be great. Thanks.
To set the color of text you need to pass the NSForegroundColorAttributeName in the attribute as the additional parameter.
NSDictionary *dictionary = #{ NSFontAttributeName: self.font,
NSParagraphStyleAttributeName: paragraphStyle,
NSForegroundColorAttributeName: self.textColor};
I've made a UIView with drawRect: containing only the code you provided
- (void)drawRect:(CGRect)frame
{
NSMutableParagraphStyle *textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
textStyle.lineBreakMode = NSLineBreakByWordWrapping;
textStyle.alignment = NSTextAlignmentCenter;
UIFont *textFont = [UIFont systemFontOfSize:16];
NSString *text = #"Lorem ipsum";
// iOS 7 way
[text drawInRect:frame withAttributes:#{NSFontAttributeName:textFont, NSParagraphStyleAttributeName:textStyle}];
// pre iOS 7 way
CGFloat margin = 16;
CGRect bottomFrame = CGRectMake(0, margin, frame.size.width, frame.size.height - margin);
[text drawInRect:bottomFrame withFont:textFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentCenter];
}
I don't see any difference between the outputs of these two methods. Maybe the problem is somewhere else in your code?

How to obtain adjusted font size from UILabel when adjustsFontSizeToFitWidth is YES in iOS 7?

I have a label that is set to adjustsFontSizeToFitWidth = YES and I need to get the actual displayed font size.
Now iOS 7 deprecated all methods that worked previously and all questions on SO suggest using these deprecated methods.
I will make this question a bounty as soon as I am allowed to by SO. Please do not close.
UILabel displayed fontSize in case of using adjustsFontSizeToFitWidth in iOS 7
UILabel *label=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 40)];
label.text = #" Your Text goes here into this label";
label.adjustsFontSizeToFitWidth = YES;
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithAttributedString:label.attributedText];
// Get the theoretical font-size
[attrStr setAttributes:#{NSFontAttributeName:label.font} range:NSMakeRange(0, attrStr.length)];
NSStringDrawingContext *context = [NSStringDrawingContext new];
context.minimumScaleFactor = label.minimumScaleFactor;
[attrStr boundingRectWithSize:label.frame.size options:NSStringDrawingUsesLineFragmentOrigin context:context];
CGFloat theoreticalFontSize = label.font.pointSize * context.actualScaleFactor;
NSLog(#"theoreticalFontSize: %f",theoreticalFontSize);
NSLog(#"AttributedString Width: %f", [attrStr size].width);
double scaleFactor=label.frame.size.width/([attrStr size].width);
double displayedFontSize=theoreticalFontSize*scaleFactor;
NSLog(#"Actual displayed Font Size: %f",displayedFontSize);
// Verification of Result
double verification=(displayedFontSize * [attrStr length]);
NSLog(#"Should be equal to %0.5f: %0.5f ", [attrStr size].width/17.0, label.frame.size.width/displayedFontSize);
Try inserting [lblObj sizeToFit] just before requesting the font size
There's a readonly property that lets you do that. You can access it like this
nameLabel.adjustsFontSizeToFitWidth = YES;
//Make sure to use the line below AFTER the line above
float fontSize = nameLabel.font.xHeight;
This will give you the font size after it has been adjusted to fit width.
You can get the font size of UILabel text using these line of code.
UILabel *lblObj = [[UILabel alloc]init];
lblObj.text = #" Your Text";
lblObj.adjustsFontSizeToFitWidth = YES;
float size = lblObj.font.pointSize; //Here You will get the actual size of the text.
float lineHeight = lblObj.font.lineHeight;
Try this one.

How to make NSStringDrawingContext shrink text?

I'm trying to use the attributed string API of iOS 6 to calculate the size of text and shrink the font size if necessary. However, I can't get it to work as the documentation says.
NSString *string = #"This is a long text that doesn't shrink as it should";
NSStringDrawingContext *context = [NSStringDrawingContext new];
context.minimumScaleFactor = 0.5;
UIFont *font = [UIFont fontWithName:#"SourceSansPro-Bold" size:32.f];
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.lineBreakMode = NSLineBreakByClipping;
NSDictionary *attributes = #{ NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle };
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:self.title attributes:attributes];
CGRect rect = [attributedString boundingRectWithSize:CGSizeMake(512.f, 512.f) options:NSStringDrawingUsesLineFragmentOrigin context:context];
NSLog(#"rect: %#, context: %#", NSStringFromCGRect(rect), context.debugDescription);
But the text doesn't shrink and is truncated. actualScaleFactor is always 1. The log results are:
rect:{{0, 0}, {431.64801, 80.447998}}, context:<NSStringDrawingContext: 0x14e85770> minimumScaleFactor:0.500000 minimumTrackingAdjustment:0.000000 actualScaleFactor:1.000000 actualTrackingAdjustment:0.000000 totalBounds:{{0, 0}, {431.64801, 80.447998}}
The result is the same if I use the actual drawing method and not the measuring method. If I remove the paragraph style, it makes the text wrap and doesn't shrink it. If I remove the paragraph style AND I choose a size that only allows one line of text, the text is truncated too instead of being shrunk. What is wrong? There is very little documentation or online resources dealing with NSStringDrawingContext. And I'm trying to avoid the use of sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: which is deprecated in iOS 7.
NSStringDrawingContext's minimumScaleFactor appears to be broken in iOS 7.
As far as I can make out, even in iOS 6, it didn't work for drawing; it worked for measuring, so you could work out what would happen in a context where it does work for drawing, like a UILabel. That way, you know the correct minimum height for the label.
Or, you could use the resulting scale factor to shrink the text yourself, in the knowledge that now it will fit.
Example:
- (void)drawRect:(CGRect)rect
{
// rect is 0,0,210,31
NSMutableAttributedString* s =
[[NSMutableAttributedString alloc] initWithString: #"This is the army Mister Jones."];
[s addAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"GillSans" size:20]}
range:NSMakeRange(0,s.length)];
NSMutableParagraphStyle* para = [[NSMutableParagraphStyle alloc] init];
para.lineBreakMode = NSLineBreakByTruncatingTail;
[s addAttributes:#{NSParagraphStyleAttributeName:para}
range:NSMakeRange(0,s.length)];
NSStringDrawingContext* con = [[NSStringDrawingContext alloc] init];
con.minimumScaleFactor = 0.5;
CGRect result =
[s boundingRectWithSize:rect.size
options:NSStringDrawingUsesLineFragmentOrigin context:con];
CGFloat scale = con.actualScaleFactor;
// ...(could have a check here to see if result fits in target rect)...
// fix font to use scale factor, and draw
[s addAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"GillSans" size:20*scale]}
range:NSMakeRange(0,s.length)];
[s drawWithRect:rect options:NSStringDrawingUsesLineFragmentOrigin context:nil];
}
In iOS 6, scale is about 0.85 and you can use it as shown to shrink the text. But in iOS 7, scale remains at 1, suggesting that no shrinkage is happening and this feature of NSStringDrawingContext is now useless. I can't tell whether that's a bug or whether the feature has been deliberately abandoned.
After googling for a long time I did not find a solution working under iOS7. Right now I use the following workaround, knowing that it is very ugly. I render a UILabel in memory, take a screenshot and draw that. UILabel is able to shrink the text correctly.
But perhaps someone finds it useful.
UILabel *myLabel = [[UILabel alloc] initWithFrame:myLabelFrame];
myLabel.font = [UIFont fontWithName:#"HelveticaNeue-BoldItalic" size:16];
myLabel.text = #"Some text that is too long";
myLabel.minimumScaleFactor = 0.5;
myLabel.adjustsFontSizeToFitWidth = YES;
myLabel.backgroundColor = [UIColor clearColor];
UIGraphicsBeginImageContextWithOptions(myLabelFrame.size, NO, 0.0f);
[[myLabel layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[screenshot drawInRect:myLabel.frame];
Just wanted to post this solution as I have been battling with this for a couple hours. I could never get the text area to scale down and would always have the last lines of text cut off.
The solution for me was to not add a context and just set it to nil. I got this when looking at the example on this site. https://littlebitesofcocoa.com/144-drawing-multiline-strings
Note after getting the size the box would draw at using
pageStringSize = [myString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrsDictionary context:nil];
I still needed to loop through scaling down the font manually:
while (pageStringSize.size.height > 140 && scale > 0.5) {
scale = scale - 0.1;
font = [UIFont fontWithName:#"Chalkboard SE" size:24.0 *scale];
attrsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,[NSNumber numberWithFloat:1.0], NSBaselineOffsetAttributeName, nil];
pageStringSize = [myString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrsDictionary context:nil];
}

Resources