Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

I need a single-line UILabel to resize after text is changed

I have a view that I have constructed in IB which contains a UIImageView and 3 UILabels. In the top left corner there is an image working awesomely -- it scales images up to fill its area and it moves if there is a navigation bar.

The problem I'm having is that there is a UILabel to the immediate right of this image which will contain someone's name. It should resize to display all the text can, filling the available space to the right of my UIImage. It is not...it turns "Person 1" into "Per...". I do not want the font size to ever change. Fixed font size, automatic resizing to show as much of a single line of text as possible. Note that I want this label to have more space available should the user rotate their iPhone to landscape format. I know how to set the shouldRotateWhatever thing...it's the settings on the UILabel that I'm having problems with.

Ideally, I'm looking for a solution that I can implement in Interface Builder. I have tried every combination and permutation of Label Attributes and Label Size settings that I can imagine and nothing seems to work. If the solution must be a programmatic one, I could handle that, but would love if someone could explain how to do it in IB.

iMac, Mac OS X (10.5.6)

Posted on May 22, 2009 7:05 PM

Reply
7 replies

May 22, 2009 8:26 PM in response to sneakyimp

I don't have a solution you'll love so I'll try for like.

There's no way to do what you want in IB, since the only UIView feature that applies is sizeToFit, which is a method, not a property. Generally IB can only initialize properties; it can't schedule methods to run.

The problem with sizeToFit is that you can't set a limit. But UIView also has a method to measure the required size without changing it: sizeThatFits (right above the previous link in the UIView reference). So as soon as you set label.text, you can get the length needed and reset the frame. If the new frame length is over your limit, your code can decide what to do.

One caveat: I've played with sizeThatFits a little but not enough to know if it will track different font families and sizes. The doc says sizeThatFits measures the "subviews" of the receiver, so I'm wondering what it's measuring in the case of text. If you have any problems with that method you can use one of the NSString UIKit Additions methods, which are intended for text measurement. Those methods are a little more work because you'll need to pass the font info, but I'm sure they'll do the job.

Let me know which method you choose. I have some test code around here somewhere I can post if you need it.

May 23, 2009 12:18 AM in response to sneakyimp

I did some more tests with sizeToFit and sizeThatFits. For a one line label they track just fine. When I vary the font size, the origin of the label frame doesn't move, and the width and height expand or contract correctly. I think sizeThatFits will do the job for you.

#define MAXLABELWIDTH 200
- (void)setAndResizeLabel:(UILabel*)aLabel withText:(NSString*)someText {
aLabel.text = someText;
// get the size that's big enough for the new text
CGSize newSize = [aLabel sizeThatFits:aLabel.frame.size];
if (newSize.width > MAXLABELWIDTH) {
// handle the overflow some way
newSize.width = MAXLABELWIDTH;
}
// resize the label
CGRect frame = aLabel.frame;
frame.size = newSize;
aLabel.frame = frame;
}

The above works for landscape too.

- Ray

May 23, 2009 2:20 PM in response to RayNewbie

I suppose I do like this. I certainly appreciate the help and can understand the example you've given.

I'm wondering a couple of things:
1) Is there some function I can define/override/augment in my UIViewController which could take advantage of this code when the user changes their orientation? I'm guessing I'll have to move/redraw my view when the user rotates their iPhone.

2) Is there some way to sniff out the width/height currently available to a given UIView? I see you've defined MAX LABELWIDTH as a constant...I'd prefer to grab it live as my code runs -- unless you think that's a bad idea.

May 23, 2009 5:01 PM in response to sneakyimp

sneakyimp wrote:
I'm wondering a couple of things:
1) Is there some function I can define/override/augment in my UIViewController which could take advantage of this code when the user changes their orientation? I'm guessing I'll have to move/redraw my view when the user rotates their iPhone.

Well I'm not sure what kind of advantage you're thinking about. When I tested that code, I pinned the label to the upper left corner (left and upper struts on in IB), and turned both springs off. That resulted in a label with exactly the same dimensions and relative position in both portrait and landscape regardless of the text or font.

The best (and easiest) rotatable UI is made by adjusting the struts and springs in the IB Size Inspector. It takes a little practice. My graphic ability is quite limited, so the effect of these settings was never obvious to me. But I just use the orientation switch (the upper-right-corner curved arrow in the IB editor window) to see what's going to happen. Anyway the user experience is usually far superior if you can avoid using code to change the layout when the orientation changes.

Btw, if you meant you'd like to use the label to detect the orientation, you should use UIDevice.orientation for that purpose.

2) Is there some way to sniff out the width/height currently available to a given UIView?

For any descendant of UIView, just look at view.bounds.size.width and height (The compiler doesn't seem to like four dots, so break that up into two lines as show in the code). You can normally use the frame instead of the bounds, but never use view.frame.origin if the view has been rotated.
I see you've defined MAX LABELWIDTH as a constant...I'd prefer to grab it live as my code runs -- unless you think that's a bad idea.

There's no reason it needs to be a constant. I only meant to show where you would limit the width.

Btw, I never asked why you cared about the width of the label, since labels almost always have a transparent background. I generally make the label as wide as possible and limit the width of the text as needed (using the NSString measurement methods at the link I posted). I assume you'd only need to limit the label itself if the background were opaque. None of my business, of course. You asked for an expanding label and now you have one 🙂

May 23, 2009 5:19 PM in response to RayNewbie

I'm not sure what you mean by 'springs'. When I said "take advantage" or your code, what I meant was that your code seems to wonderfully size a UILabel exactly once. What happens when I load my view in portrait orientation and render it, then the user turns their iPhone to landscape and it must re-render? Does loadView get called repeatedly? What about viewDidLoad? I was wondering where best to apply your code. BTW, I did notice loadView running multiple times when I instantiated my view from IB. Weird.

The reason I care about the width of the label is as follows. I added the UILabel to my view in IB. I typed "name" in it. When my app runs, the viewDidLoad routine in the appropriate UIViewController grabs some data from one of its instance vars and sets an image and a couple of labels. the Name label looks great if your name is "Tom" but only shows a few chars if your name is "John Jacobs Jingleheimerschmidt".

May 23, 2009 8:28 PM in response to sneakyimp

sneakyimp wrote:
I'm not sure what you mean by 'springs'.

Ok, then I assume this also made no sense to you:
avoid using code to change the layout when the orientation changes.

By 'springs' I was referring to autosizing settings in the IB Size panel (Tools->Size Inspector). The springs are the horizontal and vertical red, double arrow lines inside the left square in the Autosizing section. When the horizontal spring is "on" (full red), the width of your label will contract or expand in proportion to the container view's width. If you click on the spring to turn it off (dim red), the label will maintain its same width regardless of any change in the container view's width.
What happens when I load my view in portrait orientation and render it, then the user turns their iPhone to landscape and it must re-render?

If the horizontal spring is off, the label doesn't re-render, it simply rotates. After the rotation, the width will be unchanged.

Before we discuss this further, I think you would benefit from playing with the autosizing controls in IB. Correct autosizing is the key to designing a UI that changes orientation correctly. There's a good explanation of the IB controls with illustrations under Setting a View’s Autosizing Behavior in the Interface Builder User Guide.

See if you can make your view work in both orientations using only the struts and springs for each subview; i.e. without any code. After you get some practice with autosizing, I think more of my previous post will make sense, ok?

May 27, 2009 4:02 PM in response to RayNewbie

I had tried setting the autosizing behavior in IB. In fact, I spent hours trying every combination and permutation I could think of. I changed the settings on the UILabel, on the image, on the UIView parent of both. Nothing seemed capable of expanding the UILabel. I eventually tried resizing the UILabel so that it was wider in my IB nib and this seems to have helped. Keep in mind that I just added the UILabel originally and all it contained was "Name". Why this UILabel could not expand to contain longer strings is still a mystery to me.

Seems to me the 'springs' to which you refer really only work if your UIView objects are fairly large in relation to their parent View or the Application window itself.

I still find myself wondering if there is any event or function in which I could detect the current iPhone orientation (i.e., landscape or portrait) and manually render my view accordingly. I'm guessing the answer is no -- or, if yes it is probably quite involved.

In the meantime, I have had some luck getting UILabels to automatically resize quite well using something like this:

NSString *text = @"Some short or possibly longer text string";
CGSize withinSize = CGSizeMake(tableView.contentSize.width-20, 1000);
CGSize size = [text sizeWithFont:font
constrainedToSize:withinSize
lineBreakMode:UILineBreakModeWordWrap];
CGRect frame = CGRectMake(0,0,size.width, size.height);

UILabel *custLabel = [[UILabel alloc] initWithFrame:frame];
custLabel.text = text;
custLabel.font = font; // defined previously as an instance var
custLabel.numberOfLines = 0;
[cell.contentView addSubview:custLabel]; // in this case, a UITableViewCell
[custLabel release];

I need a single-line UILabel to resize after text is changed

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.