Search code examples
iosobjective-cuser-interfacewidthuisegmentedcontrol

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


Starts like this, which I like:
Screenshot1.png

But then I add a segment and this happens:
Screenshot2.png The width is set in IB, not in code.

All I need is a method to calculate width on the fly. At the end, it would do something like this:

control.width = (labelWidths + marginWidths);
// where marginWidths = (marginWidth * control.numberOfSegments)

Solution

  • 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)];
    }