How much text a label can adopt - ios

In my project there is a specific requirement, When UILabel's text gets truncated I need to give view more functionality. Initially there will be a CGRect given. Accordingly we need to show the label if text truncated we need to at the end of label ...View more text should be shown. Upon tapping on ...View more view more I need to make my label bigger. So I m doing
NSMutableString *truncatedString = [text mutableCopy];
[truncatedString appendString:ellipsis];
NSRange range = NSMakeRange(truncatedString.length - (ellipsis.length + 1), 1);
do {
[truncatedString deleteCharactersInRange:range];
range.location--;
[self setText:truncatedString];
} while ([self isTextTruncated]);
it works fine for smaller text since I m using it for UITableViewCell. It is lagging for bigger texts since above operation happens for every time. So I want to know the text that is adopted in UILabel so that I can do any operation with new text. Any help would be greatly appreciated.
EDIT:
I have a label and bigger text. say my text is
"Apple Inc. is an American multinational corporation headquartered in Cupertino, California, that designs, develops, and sells consumer electronics, computer software, online services, and personal computers." if my label would adopt only "Apple Inc. is an American multinational corporation.." I need this text alone

Use this method to calculate height that would be required for the text to get fit into the provided UILabel:
- (CGFloat)getLabelHeight:(UILabel*)label
{
CGSize constraint = CGSizeMake(label.frame.size.width, 20000.0f);
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingBox = [yourString boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:context].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;
}
Compare the returned height with height of your label:
CGFloat heightRequired = [self getLabelHeight:myLabel];
if(myLabel.frame.size.height < heightRequired) {
//you need to show more because the text is more than the label width and height.
}
else {
//you don't need to show more because the text is not more than the label width and height.
}
EDIT: The purpose of comparing height is to check whether frame is enough to show text or not. So, even if you want to increase the width of label to show more text, it will give you desired result.

You need to calculate size of text as bellow and if returned size is bigger than text field size than you need to show ...View more
CGSize requiredSize = [text sizeWithFont:withFont constrainedToSize:textViewSize lineBreakMode:lineBreakMode];

If you didn't find an answer, i recommend this workaround:
By trying using a text with known length, get the max number of characters that the label fits,and use that value to do the following:
int maxNumOfChar = 15; //For example
if (text.length > maxNumOfChar){
NSString* viewMore = #"...View More";
text = [[text substringToIndex:maxNumOfChar - [viewMore length]] stringByAppendingString: viewMore];
}

Related

How can I set same font-scale for multiple labels depending on which one is autoshrinked?

I have 4 label views of which one is supposed to show large numerical values and is set for autoshrink.
My requirement would be to set the same font-scaling or size this label has, after its been auto-adjusted to fit its content, to the other labels too so that the text content looks uniform throughout.
Setting the minimum scale factor didn't help for the other labels because they have content within the frame limits.
There's no way to do this directly, since querying the font of the label where the text has been shrunk to fit, still shows the original font size. You have to do it by iterating over smaller and smaller font sizes until you find the size that fits in your label, and then use that font size to adjust your other labels. In my example below, labelLong is the one whose text can shrink, and labelShort is the one whose text doesn't need to shrink.
-(void)updateFont {
NSStringDrawingContext *ctx = [NSStringDrawingContext new];
ctx.minimumScaleFactor = 1.0;
UIFont *startingFont = self.labelLong.font;
NSString *fontName = startingFont.fontName;
CGFloat startingSize = startingFont.pointSize;
for (float i=startingSize*10; i>1; i--) { // multiply by 10 so we can adjust font by tenths of a point with each iteration
UIFont *font = [UIFont fontWithName:fontName size:i/10];
CGRect textRect = [self.labelLong.text boundingRectWithSize:self.labelLong.frame.size options:NSStringDrawingTruncatesLastVisibleLine attributes:#{NSFontAttributeName:font} context:ctx];
if (textRect.size.width <= [self.labelLong textRectForBounds:self.labelLong.bounds limitedToNumberOfLines:1].size.width) {
NSLog(#"Font size is: %f", i/10);
self.labelShort.font = [UIFont fontWithName:fontName size:i/10];
break;
}
}
}

Display various size of text block in Xcode

I'm creating a questionnaire app. In the view controller, I have a question displayed as a label and then I have three answers below displayed as labels too. The problem is that if the question has multiple lines, it's being displayed in one line anyway, and if I stretch out the label, it overlaps with the answers.
What is the right way to display the question so that the answers appear just under the question, no matter how long the question is?
Try setting the property numberOfLines to 0 for your question label, it will allow an unlimited number of lines to be displayed. Then calculate the text size using iOS 7 method boundingRectWithSize:options:context::
CGRect questionFrame = self.questionLabel.frame
CGFloat maxWidth = questionFrame.size.width;
NSString *questionText = [NSString stringWithString:#"lorem ipsum"]; // your text
UIFont *font = self.questionLabel.font; // your label font
// Temporary attributed string
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:questionText attributes:#{ NSFontAttributeName: font }];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){ maxWidth, CGFLOAT_MAX }
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
// Assign the new height
questionFrame.size.height = rect.size.height;
self.questionLabel.frame = questionFrame;
Then move your answers label one by one under the question label:
CGRect answerFrame = self.answerLabel1.frame;
answerFrame.origin.y = questionFrame.origin.y + questionFrame.size.height;
self.answerLabel1.frame = answerFrame;
answerFrame.origin.y = answerFrame.origin.y + answerFrame.size.height;
self.answerLabel2.frame = answerFrame;
answerFrame.origin.y = answerFrame.origin.y + answerFrame.size.height;
self.answerLabel3.frame = answerFrame;
If you want to use UILabel you must first of all set its "numberOfLines" property to 0. The value "0" means "no limit", so the text will be broken in the right number of lines. Then you have the problem of determining the right size of the label, as the height changes depending on the length of the question. In such case you can use the function - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context (available with iOS7 and that replaces the - (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakMode available before iOS7).
Once you determine the height of the label you change its frame accordingly. Obviously you must change the origin of the answers labels to avoid that they conflict with the question label.
Note that if both questions and answers are too long, you may end up with an overall labels height taller than the screen height. In such case you should put all these labels inside a scrollview or as cells of a table view, to allow scrolling.

Set UILabel Align top is not working in the cell

I have a problem to implement the text vertical alignment inside a table cell.
What I want to do is based on the length of the text I want to display a message top aligned in side one UILabel inside a cell.
For example if the message is only one line
The text should align top:
And if there are two rows then it should look like this:
At the beginning what I can see is like this
So I have searched the web and what I found is to
use the
[label1 sizeToFit];
But the problem with that is within the table view cell it is not always necessarily called especially when I switched to and from another tab view.
Then I tried to generate the label on the fly by code, but the problem is that let alone the complicated process of setting up the font format I want. I have to manage whether the label has been inserted or not then reuse it every time cellForRowAtIndexpath is called.
And more weirdly, once I select the row. The alignment is switched from the one you see in the first picture to the third one. It also happens when I switched to a different tab view and switch back to the view.
I was wondering if anybody has encountered such issue and have a solution to the problem.
Thank you for your reply in advance.
Edit:
#βḧäṙℊặṿῗ, what you said I have tried. It successfully align the label text if there is only one line. My situation is that, since I have multiple tab views. Once I switch back and forth between tabs view. The alignment just restored to centre-vertical alignment again. It also happens when I selected the row. Any idea?
Try this
// label will use the number of lines as per content
[myLabel setNumberOfLines:0]; // VERY IMP
[myLabel sizeToFit];
EDIT:
As you have one extra condition that maximumly display two lines then you need to set setNumberOfLines: to 2
[myLabel setNumberOfLines:2];
Create UILabel+Extras and add following methods to this class.
- (void)alignTop{
CGSize fontSize = [self.text sizeWithAttributes:#{NSFontAttributeName:self.font}];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGRect rect = [self.text boundingRectWithSize:CGSizeMake(finalWidth, finalHeight) options:NSStringDrawingTruncatesLastVisibleLine attributes:#{NSFontAttributeName:self.font} context:nil];
CGSize theStringSize = rect.size;
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i< newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#" \n"];
}
Call this method like this..
[YOUR_LABEL alignTop];
Either You set no of Lines to be 0 like
[yourLabelObject setNumberOfLines:0];
[yourLabelObject sizeToFit];
Or
You can find the height of label at run time depending upon textString length.
Following method will return you the size(height & width) of label for length of text string.
here width is fixed and only height will change :
- (CGSize) calculateLabelHeightWith:(CGFloat)width text:(NSString*)textString andFont:(UIFont *)txtFont
{
CGSize maximumSize = CGSizeMake(width, 9999);
CGSize size = [textString sizeWithFont:txtFont
constrainedToSize:maximumSize
lineBreakMode:UILineBreakModeWordWrap];
return size;
}
Yo need to calculate frame of label each time when u r doing to set text and set frame of label
I hope this will helps you.
Might sound a bit silly but this is my approach: For any field that needs to be top aligned I fill the text up with multiple "\n"s. This causes the text to be automatically top aligned. Pretty much the same as Mehul's method above.
http://i.stack.imgur.com/8u5q4.png

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