This question already has answers here:
Vertically align text to top within a UILabel
(51 answers)
Closed 8 years ago.
On iOS 5.
I have a UILabel element that is originally placed in a nib. I want the x placement to stay fixed. I want it to take either 1 line, or 2 lines. If more than two lines, it should use the line break setting to show the ellipsis.
I use numberOfLines property and -sizeToFit
If I set the UILabel's numberOfLines property to 0, it will correctly see that for some text, there is not enough room and will wrap it to a second line after calling -sizeToFit but in the rare case that the line is long enough to stretch to 3 lines, I get three lines, which I don't want. If I set numberOfLines property to 2, it actually stretches the whole thing out onto one line and elongates my initial frame set in the nib to be much wider.
CGRect titleFrame = [[self titleLabel] frame];
[[self titleLabel] setNumberOfLines:0];
[[self titleLabel] setText:newProductTitleText];
[[self titleLabel] sizeToFit];
CGRect newTitleFrame = [[self titleLabel] frame];
The CGRect are just there for me to be able to calculate things after the fact. So setting numberOfLines to 0 works, and will not change the original origin.x in the frame, and will break long text into multiple lines, but will not constrain it to 2 lines. Setting numberOfLines property to 2, which, when I read the Apple docs
This property controls the maximum number of lines to use in order to fit the label’s text into its bounding rectangle. The default value for this property is 1. To remove any maximum limit, and use as many lines as needed, set the value of this property to 0.
It seems I should be able set this to two and still have it work. I would expect sizeToFit to expand in a positive X and Y direction when expanding to fit all the text but it is expanding in a negative X direction when numberOfLines is set to other than 0.
ETA: the "Autosize" struts are set to upper and left to fix it at a min x,y.
Thanks for any insight.
I had a similar problem where -[UILabel sizeToFit] was not respecting the max width I set when numberOfLines was set to 2. Here's how I solved that problem:
CGFloat titleMaxWidth = 200;
CGFloat titleMinHeight = 30;
CGFloat titleMaxHeight = 40;
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, titleMaxWidth, titleMaxHeight)]; // alternatively, you could do this in a nib
titleLabel.numberOfLines = 0;
titleLabel.text = #"The title label will be sized appropriately with this technique.";
titleLabel.font = [UIFont boldSystemFontOfSize:16];
[titleLabel sizeToFit];
titleLabel.numberOfLines = 2;
if (titleLabel.height > titleMaxHeight)
{
titleLabel.height = titleMaxHeight;
}
else if (titleLabel.height < titleMinHeight)
{
titleLabel.height = titleMinHeight;
}
As you can see, I also wanted a minimum height for my label, as -sizeToFit often makes the label really small, but you could disregard that code if you don't care about a minimum height. The "magic number" of 40 for the titleMaxHeight comes from experimentation and finding out that a 2 line label with this font really only needs 40px. In this code, -sizeToFit is mainly used to keep the text within the width and determine whether the initial height of 40 can be reduced when we have a short string of text.
I used the UIFont property lineHeight:
CGFloat labelHeight = label.font.lineHeight*label.numberOfLines;
Related
I wanted to know is it possible to get the height of a multi line UILabel? I'm developing a messaging application and wanted to achieve something like the iPhone messaging application.
You can get the property with label.frame.size.height
You probably want the -[UILabel sizeThatFits:] method. Here's what you do. Let's say your UILabel is in the variable myLabel, and you've already set its width to whatever you want.
myLabel.text = #"This is my very long message which will probably need multiple lines to be displayed because it is very long.";
CGRect bounds = myLabel.bounds;
// Create a size that is the label's current width, and very very tall.
CGSize prototypeSize = CGSizeMake(bounds.size.width, MAXFLOAT);
// Ask myLabel how big it would be if it had to fit in prototypeSize.
// It will figure out where it would put line breaks in the text to
// fit prototypeSize.width.
CGSize fittedSize = [myLabel sizeThatFits:prototypeSize];
// Now update myLabel.bounds using the fitted height and its existing width.
myLabel.bounds = (CGRect){ bounds.origin, CGSizeMake(bounds.size.width, fittedSize.height) };
If you call
[label sizeToFit];
it will resize the UILabel to the minimum size needed to hold all the content. Then you can just do label.frame.size.height to get the height of the label with that amount of text in it.
Is there an equivalent to NSString's sizeWithFont: method that can be used for calculating the height of text in a UITectView for a given width? All of the methods from NSString only operate on a single line from what I can tell.
From Apple's reference for these NSString methods, you could use -sizeWithFont:constrainedToSize: or -sizeWithFont:constrainedToSize:lineBreakMode: for "Computing Metrics for Multiple Lines of Text".
CGSize size = [theString sizeWithFont:font
constrainedToSize:CGSizeMake(width, 100000)];
return size.height;
For UITextView, all you have to do is call -sizeToFit on the view, and it will automatically resize its height until it can fit all the text available. All you need to do is set the width of the text view, set the text, then call -sizeToFit. The text view will resize its height just enough to fit all the text.
UPDATE:
Apparently text views only shrink when there's excess height, but they don't grow if there's insufficient height to display all the text. In addition, once you call -sizeToFit, the text view's y coordinate is reset back to 0.0f. So here's what you do:
CGFloat textViewWidth = 300.0f;
CGFloat textViewPadding = 10.0f;
UITextView * textView = [[[UITextView alloc] init] autorelease];
textView.text = ...; // Really long string
textView.frame = CGRectMake(0.0f, 0.0f, textViewWidth, CGFLOAT_MAX);
[textView sizeToFit]; // Shrinks the height to fit all the text
textView.frame = CGRectMake(textViewPadding, textViewPadding,
textViewWidth, textView.frame.size.height);
[self.view addSubview:textView];
First, you set the frame just so you can set the width like you want it. You use CGFLOAT_MAX to pretty much indicate infinite height. Next, calling -sizeToFit shrinks the height until it just fits all the text. However, it also resets the y coordinate, so we go ahead and set the frame again to configure the x and y coordinates—in this example, 10.0f for both x and y—, leaving the width alone and keeping the height set to whatever -sizeToFit calculated.
actually, you could use the property contentSize.
Let's say I have two UILabels positioned vertically below each other in a UITableViewCell. The line break mode is set to UILineBreakModeWordWrap for both.
Their horizontal size is fixed, but both can stretch vertically based on how much text they display. How do I position the one that's below dynamically so that they would never overlap?
i use this function to get the height of the text, and then set the second label height to the result.
- (CGFloat)HeightOfText:(NSString *)textToMesure widthOfLabel:(CGFloat)width
{
UIFont *textFont = [UIFont systemFontOfSize:16];
CGSize ts = [textToMesure sizeWithFont:textFont constrainedToSize:CGSizeMake(width-20.0, FLT_MAX) lineBreakMode:UILineBreakModeWordWrap];
return ts.height+25.0; //you can change the last number to fit the space you wish to have between the labels.
}
and you use it like that:
NSString *firstLabelText = #"the text";
CGFloat textSize = [self HeightOfText:firstLabelText widthOfLabel:firstLabel.frame.size.width];
then use "textSize" to set the second label height.
hope it will help.
Try -[UILabel sizeThatFits:]. Say you're laying the labels out in a 300px wide column, you can pass in a size of (300,99999), and it'll tell you the size it actually needs to fit that text in. You can use that to adjust the frames of your labels appropriately.
I've done something similar by resetting the frame of the view below (assuming you've already done sizeWithFont or sizeThatFits and resized the labels). Similar to this:
- (void)positionView:(UIView *)aView belowView:(UIView *)anotherView withPadding:(int)padding
{
CGRect aFrame = aView.frame;
if (anotherView)
{
aFrame = (anotherView.frame.origin.y + anotherView.frame.size.height);
}
aFrame.origin.y += padding;
aView.frame = aFrame;
}
when you enter a too long sentence for the iphone it automaticaly add a "..." at the end to show you there is other stuff you don't see right. I want to delete those "...".
image :
alt text http://img691.imageshack.us/img691/2159/screenshot20100602at095.png
Well, I'm assuming you're using a label. Look into the "lineBreakMode" property. Your solution will probably involve some combination of that property in conjunction with the "numberOfLines" property. For example, setting the "numberOfLines" property to 0 will automatically increase the height of a label to fit all text. So using that with a UILineBreakModeWordWrap would probably do the trick.
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
label.text = #"Light beer 5% 10oz Glass served cold";
[label release];
You have several options for that:
Set label's lineBreakMode property to UILineBreakModeClip - that way your sentence will just be clipped without "..." on the end
Set label's adjustsFontSizeToFitWidth property to YES - label will automatically reduce font size to fit string into available space
Make your UILabel have multiple lines - set its numberOfLines property to 0 and lineBreakMode to UILineBreakModeWordWrap. Although with this approach your label's height must be big enough to contain several lines...
Is there a way to get the correct size of an NSString using:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
that doesnt get thrown off by 2 or 3 hundred character strings. At the moment if I try to use this method on these long strings it incorrectly calculates them and I end up with lots of whitespace at the bottom of the UITextView.
I've tried using UILineBreakModeWordWrap and UILineBreakModeCharacterWrap.
the resizing is being done in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat result = 44.0f;
NSString* text = nil;
CGFloat width = 0;
CGFloat tableViewWidth;
CGRect bounds = [UIScreen mainScreen].bounds;
tableViewWidth = bounds.size.width;
width = tableViewWidth - 150;
text = stringWithLongWords;
if (text) {
CGSize textSize = { width, 20000.0f };
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:10.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];
size.height += 50.0f;
result = MAX(size.height, 44.0f+30.0f);
}
return result;
}
UITextView is not exactly like a UILabel wrapped in a UIScrollView. It has line spacing different from the font size and margins that sizeWithFont:constrainedToSize:linkBreakMode: doesn't account for.
Knowing your font size you might be able to calculate the # of lines and take line spacing into account. You can guess at the margins and try to trick sizeWithFont: to give a more useful answer.
The popular solutions seem to be:
just use a UILabel if you don't need any UITextView functionality
if you need hyperlinks, overlay UIButtons that look like hyperlinks over a UILabel
use an off-screen UITextView and its sizeToFit method to get a real answer
I had no luck w/ the 3rd option but it sounds like it should work, so perhaps I did something wrong.
I'm going to try using a UILabel and overlaying buttons for hyperlinks. We'll see how that turns out.
If that fails, there is always the option taken by Loren Brichter (of Tweetie fame): draw everything into a UIView yourself using CoreGraphics.
Good luck!
Check out this post How do I size a UITextView to its content?
It looks like textView.contentSize.height should work (with the caveat that the the correct contentSize is only available after the UITextView has been added to the view with addSubview)
You said that you have a UITableView with differing heights. Have you set the reuse identifier to the same thing for all of the cells? It could be that older cells with their height already set are being reused. If this is the problem, you should resize the cell again when it's being reused.
The best solution I have found so far is to have a separate hidden UITextView with the same font settings, and set its text. After that its contetSize should be accurate.
The width you are using is the width for your UITextView... but you aren't concerned with that width, you are concerned with the width of the actual text area nested inside the text view.
UITextViews, by default, have padding around their borders to produce a space in-between the typed text and the edge of the UITextView a few pixels wide (and long for the top)... To get the correct size you shouldn't use
textView.frame.size.width
but rather,
textView.frame.size.width-(textView.contentInset.left+textView.contentInset.right+textView.textContainerInset.left+textView.textContainerInset.right+textView.textContainer.lineFragmentPadding/*left*/+textView.textContainer.lineFragmentPadding/*right*/)
^Which takes the width of the UITextView and subtracts out all the padding so you are left with the width of just the type-able text area.
Same goes for height except for lineFragmentPadding doesn't have a bottom so you only subtract it out once instead of twice.
The final code is something like this:
CGSize textViewContentSize = CGSizeMake(theTextView.frame.size.width-(theTextView.contentInset.left+theTextView.contentInset.right+theTextView.textContainerInset.left+theTextView.textContainerInset.right+theTextView.textContainer.lineFragmentPadding/*left*/+theTextView.textContainer.lineFragmentPadding/*right*/), theTextView.frame.size.height-(theTextView.contentInset.top+theTextView.contentInset.bottom+theTextView.textContainerInset.top+theTextView.textContainerInset.bottom+theTextView.textContainer.lineFragmentPadding/*top*//*+theTextView.textContainer.lineFragmentPadding*//*there is no bottom padding*/));
CGSize calculatedSize = [theTextView.text sizeWithFont:theTextView.font
constrainedToSize:textViewContentSize
lineBreakMode:NSLineBreakByWordWrapping];
CGSize adjustedSize = CGSizeMake(ceilf(calculatedSize.width), ceilf(calculatedSize.height));
Inspired by #MrNickBarker's answer, here's my solution:
CGFloat width = 280.0f;
UITextView *t = [[UITextView alloc] init];
[t setFont:[UIFont systemFontOfSize:17]];
[label setText:#"some short or long text, works both"];
CGRect frame = CGRectMake(0, 0, width, 0);
[t setFrame:frame];
// Here's the trick: after applying the 0-frame, the content size is calculated and can be used in a second invocation
frame = CGRectMake(0, 0, width, t.contentSize.height);
[t setFrame:frame];
The only issue remaining for me is that this doesn't work with modified insets.
Still can't believe such twists are required, but since -[NSString sizeWithFont:forWidth:lineBreakMode:] does not respect insets, paddings, margins, line spacings and the like, it seems this is the only working solution at the moment (i.e. iOS 6).