Two lines of text in a UISegmentedControl

herzian picture herzian · Nov 8, 2013 · Viewed 7.6k times · Source

Try as I might, I can't solve a UISegmentedControl bug for an iOS7 iPhone app.

When I create the segmented control, I use this code:

NSArray *segmentedControlItemArray = [NSArray arrayWithObjects: @"Nominal:\n27 inch", @"Actual:\n700c x 23mm", @"Actual:\n700c x 20mm", nil];
_wheelDiameterSegmentedControl = [[UISegmentedControl alloc] initWithItems:segmentedControlItemArray];
_wheelDiameterSegmentedControl.frame = CGRectMake(0, 102, 290, 50);
_wheelDiameterSegmentedControl.selectedSegmentIndex = 0;
_wheelDiameterSegmentedControl.tintColor = [UIColor colorWithRed:0.35 green:0.4 blue:0.9 alpha:1.0];

for (id segment in [_wheelDiameterSegmentedControl subviews]) {
    for (id label in [segment subviews]) {
        if ([label isKindOfClass:[UILabel class]]) {
            UILabel *titleLabel = (UILabel *) label;
            titleLabel.numberOfLines = 0;   
        }   
    }  
}

[_wheelDiameterSegmentedControl addTarget:self
                     action:@selector(pickOne:)
           forControlEvents:UIControlEventValueChanged];

[_wheelDiameterMenuContainer addSubview:_wheelDiameterSegmentedControl];

Sadly, I can't post images, or I would show you a picture of exactly the control I want: each of the segments in the UISegmented Control has two lines of text, with a line break exactly where I asked for it.

On rotation, though, I'd like to keep the segmented control full-width, and the line breaks look silly in segments that wide. So, in willAnimateRotationToInterfaceOrientation, I've included the following code, with no line breaks in the strings:

    [_wheelDiameterSegmentedControl setFrame:CGRectMake(0, 102, 450, 50)];
    [_wheelDiameterSegmentedControl setTitle:@"Nominal: 27 inch" forSegmentAtIndex:0];
    [_wheelDiameterSegmentedControl setTitle:@"Actual: 700c x 23mm" forSegmentAtIndex:1];
    [_wheelDiameterSegmentedControl setTitle:@"Actual: 700c x 20mm" forSegmentAtIndex:2];

And once again, if I could insert an image, I would show you an image of exactly what I want: a wide UISegmented Control with no line breaks in the labels (1 line of text per label).

Here's where I run in to trouble. My choices, when I rotate back to portrait, seem to be:

1 line of label text, truncated, with the format

"Actual: 7..."

when I just reset the size of the UISegmentedControl using

[_wheelDiameterSegmentedControl setFrame:CGRectMake(0, 102, 290, 50)];

2 lines of label text, truncated, with the format

"Actual:
700c x ..."

when I reset the size and also reset the string values and rerun the loop of code that sets the label's numberOfLines to 2, using

    NSArray *segmentedControlItemArray = [NSArray arrayWithObjects: @"Nominal:\n27 inch", @"Actual:\n700c x 23mm", @"Actual:\n700c x 20mm", nil];

    [_wheelDiameterSegmentedControl setTitle:[segmentedControlItemArray objectAtIndex:0] forSegmentAtIndex:0];
    [_wheelDiameterSegmentedControl setTitle:[segmentedControlItemArray objectAtIndex:1] forSegmentAtIndex:1];
    [_wheelDiameterSegmentedControl setTitle:[segmentedControlItemArray objectAtIndex:2] forSegmentAtIndex:2];
    for (id segment in [_wheelDiameterSegmentedControl subviews]) {
        for (id label in [segment subviews]) {
            if ([label isKindOfClass:[UILabel class]]) {
                UILabel *titleLabel = (UILabel *) label;
                titleLabel.numberOfLines = 2;
            }
        }
    }
    [_wheelDiameterSegmentedControl setFrame:CGRectMake(0, 102, 290, 50)];

3 lines of label text, with the format

"Actual:
700c x
20mm"

which is what I get when I replace the forced numberOfLines = 2 above with the numberOfLines = 0 that worked when I set up the UISegmentedControl in the first place.

What I'd like is what I get when I create the control, which is

"Actual:
700c x 20mm"

But no matter what I've tried (putting the string and numberOfLines code into willRotateToInterfaceOrientation or didRotateFromInterfaceOrientation; re-setting the frame of the UISegmentedControl before changing the text; re-setting the frame after changing the text...), I can't get my nice, neat, two-line label back. What am I missing here?

Answer

herzian picture herzian · Nov 10, 2013

Aha! Something about how the OS is handling the size of the label when it rotates is different from how it handles the label when it's created. So I forced the size of the frame of the label, using

for (id segment in [_wheelDiameterSegmentedControl subviews]) {

    for (id label in [segment subviews]) {

        if ([label isKindOfClass:[UILabel class]]) {

            UILabel *titleLabel = (UILabel *) label;

            //inserting line here, to make the frame behave nicely:
            //
            titleLabel.frame = CGRectMake(0, 0, 97, 50);
            //
            //continuing as before

            titleLabel.numberOfLines = 0;

        }
    }
}

and now it displays exactly as I want/expect. Huzzah!


If you're just trying to get two (or more) lines of text in your UISegmentedControl. Using Herzian's amazing technology! Here's just another code example, that may help people...

self.yourSlider .. set the height manually (can't easily do it in XIB)
// now use amazing Herzian technology...
for (id segment in [self.yourSlider subviews])
    for (id label in [segment subviews])
        {
        if ([label isKindOfClass:[UILabel class]])
            {
            UILabel *titleLabel = (UILabel *) label;
            titleLabel .. set the width to say 80, per your layout;
            (the width us typically too low)
            titleLabel.numberOfLines = 0;
            }
        }

thank you so much, again.