Calculate width required to fit text for a multiline UILabel? - ios

I am trying to expand the width of a 2 line UILabel to accommodate a varying amount of text. To do this I am attempting to calculate the width required and then setting it to a width constraint I have set on the label.
However, everything I have done ignores the height/word wrapping, and returns the width required for a single line of text (not 2 lines). Any ideas on how i can find the width needed if 2 or more lines are used in the label?
I have tried using the following methods to find the width:
1.Using boundingRectWithSize to determine the appropriate size for a fixed height (height of 2 lines)
CGSize maxSize = CGSizeMake(MAXFLOAT, fixedHeight);
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attributes = #{NSFontAttributeName: self.font, NSParagraphStyleAttributeName: paragraphStyle};
CGRect rect = [self.text boundingRectWithSize: maxSize
options: (NSStringDrawingUsesLineFragmentOrigin)
attributes: attributes
context: nil];
Using the sizeThatFits method to determine the appropriate size.
Thanks!

Make sure fixedHeight you are passing is sufficient enough to accommodate multiple lines. To be safe you can use following
CGSize maxSize = CGSizeMake(300, CGFLOAT_MAX); //where 300 is the width of label
Setting above height will make sure it takes into consideration multiple lines as width is limited. In previous case you were setting width to MAXFLOAT thus width was big enough to accommodate entire content in single line.

It might be silly, but make sure that 'numberOfLines' property of the UILabel is set to 0, so that the size calculation methods can use multiline options.

Related

Calculating font size on UILabel after adjusting it to fit width

I need to implement the code which will equalise font sizes between multiple UILabel's - for this I need to find smallest font size that will be visible from group of those UILabel's and apply it to the rest of them.
Here is the code I'm using to calculate actual font size of UILabel after font get's scale to fit it's width:
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
context.minimumScaleFactor = label.minimumScaleFactor;
NSDictionary *attributes = #{NSFontAttributeName : label.font};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];
[attributedString boundingRectWithSize:label.bounds.size
options:NSStringDrawingUsesLineFragmentOrigin
context:context];
CGFloat actualFontSize = label.font.pointSize * context.actualScaleFactor;
The problem is it works on for multiline UILabel.
After I try to use it on UILabel which has numberOfLines = 1 it is going crazy and giving me always original font size.
I tried removing NSStringDrawingUsesLineFragmentOrigin from options - but it is still giving me wrong results.
Does anyone encounter this problem or maybe you have different ways of getting the final font size after fitting to width ?

Text size that adjusts for any size class

I would like to have the text size change like images when using different size classes with auto layout. Thanks!
If you design for the largest screen, you can use the Autoshrink setting on the label to get the text to fit as the label decreases in size, otherwise you would have to do it via code if you want to both stretch and shrink
try this
-(CGSize)heigtForCellwithString:(NSString *)stringValue withFont:(UIFont*)font{
CGSize constraint = CGSizeMake(250,9999); // Replace 300 with your label width
NSDictionary *attributes = #{NSFontAttributeName: font};
CGRect rect = [stringValue boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:attributes context:nil];
return rect.size;
}

How to adjust UILabel font size to fit the fixed Rectangle (for iOS7 +)?

I know this question has been asked so many times and I have seen most of the solutions like
How to adjust font size of label to fit the rectangle?
How to calculate actual font point size in iOS 7 (not the bounding rectangle)?
NSString sizeWithFont: alternative in iOS7
What I want is that I want to adjust the font size in a given rect with WORD WRAPPING technique. The posts I mentioned above somehow adjust the font size but none of them is using word wrapping technique. Let say, I have a label
UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 60, 25)]; \\60px width
tempLabel.text = #"Internationalisation";
tempLabel.numberOfLines = 2;
Now it should wrap the text in one line (within 60 px) with minimum possible font. But it keeps the text in 2 lines with a bit bigger font and the text breaks up like "Internationali" in first line and "sation" in second. How can we apply word wrapping in it.
tempLabel.adjustsFontSizeToFitWidth = YES;
does not seem to work for two or more lines and
CGSize size = [self sizeWithFont:font
minFontSize:minFontSize
actualFontSize:&actualFontSize
forWidth:maxWidth
lineBreakMode:self.lineBreakMode];
is deprecated in iOS7+
So how can I resolve my Issue. Please do help. This is so important for me.

Positioning a UILabel directly beneath another UILabel

I have an addition to NSString which automatically resizes a UILabel depending on the text that's being read into it (I have a simple app showing quotations, so some are a few words, some a couple sentences). Below that quote label, I also have an author label, which (oddly enough) has the author of the quote in it.
I'm trying to position that author label directly beneath the quote label (as in, its y coordinate would be the quote label's y coordinate plus the quote label's height. What I'm seeing is some space being placed between the two labels, that depending on the length of the quote, changes size. Smaller quotes have more space, while longer quotes have less space. Here's a quick diagram of what I'm seeing:
Note the gap between the red and blue boxes (which I've set up using layer.borderColor/borderWidth so I can see them in the app), is larger the shorter the quote is.
If anyone can sift through the code below and help point me towards exactly what's causing the discrepancy, I'd be really grateful. From what I can see, the author label should always be 35 pixels beneath the quote label's y + height value.
Just to confirm: everything is hooked up correctly in Interface Builder, etc. The content of the quote's getting in there fine, everything else works, so it's hooked up, that isn't the issue.
To clarify, my question is: Why is the gap between the labels changing dependant on the quote's length, and how can I get a stable, settable gap of 35 pixels correctly?
Here's the code I'm using to position the labels:
// Fill and format Quote Details
_quoteLabel.text = [NSString stringWithFormat:#"\"%#\"", _selectedQuote.quote];
_authorLabel.text = _selectedQuote.author;
[_quoteLabel setFont: [UIFont fontWithName: kScriptFont size: 28.0f]];
[_authorLabel setFont: [UIFont fontWithName: kScriptFontAuthor size: 30.0f]];
// Automatically resize the label, then center it again.
[_quoteLabel sizeToFitMultipleLines];
[_quoteLabel setFrame: CGRectMake(11, 11, 298, _quoteLabel.frame.size.height)];
// Position the author label below the quote label, however high it is.
[_authorLabel setFrame: CGRectMake(11, 11 + _quoteLabel.frame.size.height + 35, _authorLabel.frame.size.width, _authorLabel.frame.size.height)];
Here's my custom method for sizeToFitMultipleLines:
- (void) sizeToFitMultipleLines
{
if (self.adjustsFontSizeToFitWidth) {
CGFloat adjustedFontSize = [self.text fontSizeWithFont: self.font constrainedToSize: self.frame.size minimumFontSize: self.minimumScaleFactor];
self.font = [self.font fontWithSize: adjustedFontSize];
}
[self sizeToFit];
}
And here's my fontSizeWithFont:constrainedToSize:minimumFontSize: method:
- (CGFloat) fontSizeWithFont: (UIFont *) font constrainedToSize: (CGSize) size minimumFontSize: (CGFloat) minimumFontSize
{
CGFloat fontSize = [font pointSize];
CGFloat height = [self sizeWithFont: font constrainedToSize: CGSizeMake(size.width, FLT_MAX) lineBreakMode: NSLineBreakByWordWrapping].height;
UIFont *newFont = font;
// Reduce font size while too large, break if no height (empty string)
while (height > size.height && height != 0 && fontSize > minimumFontSize) {
fontSize--;
newFont = [UIFont fontWithName: font.fontName size: fontSize];
height = [self sizeWithFont: newFont constrainedToSize: CGSizeMake(size.width, FLT_MAX) lineBreakMode: NSLineBreakByWordWrapping].height;
};
// Loop through words in string and resize to fit
for (NSString *word in [self componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
CGFloat width = [word sizeWithFont: newFont].width;
while (width > size.width && width != 0 && fontSize > minimumFontSize) {
fontSize--;
newFont = [UIFont fontWithName: font.fontName size: fontSize];
width = [word sizeWithFont: newFont].width;
}
}
return fontSize;
}
After you called size to fit on both labels, calculate the distance between their frames and change them accordingly:
[quoteLabel sizeToFit];
[authorLabel sizeToFit];
float distance = authorLabel.frame.origin.y - quoteLabel.frame.size.height;
float difference = distance - 35;
authorLabel.frame = CGRectMake(authorLabel.frame.origin.x,(authorLabel.frame.origin.y - difference),authorLabel.frame.size.width,authorLabel.frame.size.height);
The reason the gap changes is that the quote label frame changes its height dependent on its content when you call sizeToFit.
UPDATE
Given the recent developments in the comments, I think you have 3 possibilities:
resize the whitespace instead of only the words, so that the string
actually fits in the frame correctly
somehow access the CTFramesetter of UILabel to see what the actual
frame, when all is said and done, amounts to
make your own UIView subclass that handles Core Text drawing in its
draw rect method (should be easy in your case), since after all you
are trying to give to UILabel a behavior that it's not meant for
It probably is moving where you want it, but then an auto-layout constraint or a spring/strut is moving it afterwards.
EDIT:
My first thought (which I ruled out because you said that the box around the words was the label frame. In later comments, you say that this is not an actual screen shot, but just a representation of it, so it could still be correct) was that you are doing this wrong:
[_quoteLabel sizeToFitMultipleLines];
[_quoteLabel setFrame: CGRectMake(11, 11, 298, _quoteLabel.frame.size.height)];
In the first line, you are sizing the text to fit in whatever the current width of the label might be, and then you turn around in the second line and change the width of the label. So most likely, what is happening is that you are sizing the label for some smaller width, which makes it tall. You then make the label wider than it was before and the text expands to fit the wider label, leaving a blank area beneath the actual text, although the frame has not changed. This makes the space betwee the labels exactly 35 as you want, however the top label's text does not go all of the way to the bottom of its frame so the white space is more than you want. Basically, you have this:
*************
* text text *
* text text *
* *
* *
* *
*************
*************
* text text *
*************
If this is the case, then you would fix it by setting the width first, like this:
// You could put anything for the 200 height since you will be changing it in the next line anyway.
[_quoteLabel setFrame: CGRectMake(11, 11, 298, 200];
[_quoteLabel sizeToFitMultipleLines];
I ended up solving the problem by using a single UILabel, and CoreText with an NSAttributedString. Kind of a cop-out, but it works.

iOS attributed string height

I calculate the size of a UILabel (only height matters here) by dynamic length text. And I draw the label's layer's border to visualize the frame of the label. I see "padding" above and under the label text sometimes, but not always. I do not want the padding. I suspect it relates to attributed string, since I never encounter such problem in a "normal" string label.
I see this (Note the padding of the first row):
I want this:
Relevant code:
-(void)setupQuestionView
{
[self.questionView setAttributedText:[self.allContents[_itemIndex] objectForKey:#"Question"]];
// new question view height
CGSize constraint = CGSizeMake(kCellWidth - kLeftMargin - kRightMargin, FLT_MAX);
CGSize size = [self.questionView.text sizeWithFont:[UIFont systemFontOfSize: kFontSize] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
[self.questionView setFrame:CGRectMake(kRightMargin, kTopMargin, kCellWidth - kRightMargin * 2, size.height)];
[self.questionView.layer setBorderWidth:1.0f]; // debug
}
Are kLeftMargin and kRightMargin equal? I couldn't find anywhere else can possibly go wrong.

Resources