Change width of a segmented control based on titles in each segment?

As shown in below pic change Auto-Size property to 'Proportional to Content'.

enter image description here


The method in prgrmr's answer here works fine for its intended purpose, but this is not it.

Rather than adding unnecessary overhead with custom UILabel subviews,
I've modified the example code in the above link to come up with this:

- (void)resizeSegmentsToFitTitles:(UISegmentedControl *)control {
    CGFloat textWidth = 0; // total width of all text labels
    CGFloat marginWidth = 0; // total width of all margins
    NSUInteger nSegments = control.subviews.count;
    UIView *aSegment = [control.subviews objectAtIndex:0];
    UIFont *theFont = nil;

    // get font for segment title label
    for (UILabel *label in aSegment.subviews) {
        if ([label isKindOfClass:[UILabel class]]) {
            theFont = label.font;
            break;
        }
    }

    // calculate width of text in each segment
    for (NSUInteger i = 0; i < nSegments; i++) {
        NSString *title = [control titleForSegmentAtIndex:i];
        CGFloat width = [title sizeWithFont:theFont].width;
        CGFloat margin = 15;

        if (width > 200) {
            NSString *ellipsis = @"…";
            CGFloat width2 = [ellipsis sizeWithFont:theFont].width;

            while (width > 200-width2) {
                title = [title substringToIndex:title.length-1];
                width = [title sizeWithFont:theFont].width;
            }

            title = [title stringByAppendingString:ellipsis];
        }

        [control setTitle:title forSegmentAtIndex:i];

        textWidth += width;
        marginWidth += margin;
    }

    // resize segments to accomodate text size, evenly split total margin width
    for (NSUInteger i = 0; i < nSegments; i++) {
        // size for label width plus an equal share of the space
        CGFloat textWidth = [[control titleForSegmentAtIndex:i]
                             sizeWithFont:theFont].width;
        // the control leaves a 1 pixel gap between segments if width
        // is not an integer value; roundf() fixes this
        CGFloat segWidth = roundf(textWidth + (marginWidth / nSegments));
        [control setWidth:segWidth forSegmentAtIndex:i];
    }

    // set control width
    [control setFrame:CGRectMake(0, 0, (textWidth + marginWidth), 30)];
}

You can use the propertyapportionsSegmentWidthsByContent and set it toYES.


The first option looks like this:

segmentedControl.setWidth(100, forSegmentAt: 0)
segmentedControl.setWidth(50, forSegmentAt: 1)

That gives you individually sized segments while sticking to a value you define, which means you get to tweak the aesthetics as you want. The second option looks like this:

segmentedControl.apportionsSegmentWidthsByContent = true

That hands full control over to iOS, which is probably the best thing to do most of the time.

Available from iOS 5.0

hackingwithswift