Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UISegmentedControl bounds

I want to give the following aspect to an UISegmentedControl:

enter image description here

Note the gray background view, and the white background of the segmented control non selected item.

But, if I give a white background to my UISegmentedControl, I get the following:

enter image description here

Note the white square corners around the UISegmentedControl. What should I do to avoid that square corners?

Thank you in advance,

EDIT: If I change the corner radius of the UISegmentedControl's layer, as suggested by onegray, the result is better, but not perfect (note the white line at the right):

enter image description here

like image 532
neutrino Avatar asked Oct 02 '13 13:10

neutrino


3 Answers

Setting the _segmentedControl.layer.cornerRadius = 5; might help.

Update: More complex clip rect to get rid of 1px right space:

    CAShapeLayer* mask = [[CAShapeLayer alloc] init];
    mask.frame = CGRectMake(0, 0, _segmentedControl.bounds.size.width-1, _segmentedControl.bounds.size.height);
    mask.path = [[UIBezierPath bezierPathWithRoundedRect:mask.frame cornerRadius:4] CGPath];
    _segmentedControl.layer.mask = mask;

Update: Matthias Bauch provided a good explanation why this whitespace appears on the right side of the UISegmentedControl. So the simplest way to remove it is making segments of fixed size and adjusting them for proper width.

like image 98
onegray Avatar answered Nov 14 '22 18:11

onegray


If that should work for all UISegmentedControls it's a bit of a hassle. The problem is in iOS7 the 1 pt. border between two segments does not count to the size of the segment. E.g. if the frame of your UISegmentedControl is 320 pt. wide you have to remove 1 pt. and than divide by 2. And (320-1)/2 is 159.5. iOS floors this value down to 159 pt. And you end up with a 1 pt. border and two 159 pt. segments. Which is 319, and not 320. Hence the 1pt. line at the right of your segmentedControl.

There is a way to calculate the "actual" (the size of the rendering on screen) size of the segmentedControl. With that width you can then add a UIView with rounded corners below the UISegmentedControl. This code should work for all configurations, even if you have manually sized segments in your segmentedControl:

- (UIView *)addBackgroundViewBelowSegmentedControl:(UISegmentedControl *)segmentedControl {
    CGFloat autosizedWidth = CGRectGetWidth(segmentedControl.bounds);
    autosizedWidth -= (segmentedControl.numberOfSegments - 1); // ignore the 1pt. borders between segments

    NSInteger numberOfAutosizedSegmentes = 0;
    NSMutableArray *segmentWidths = [NSMutableArray arrayWithCapacity:segmentedControl.numberOfSegments];
    for (NSInteger i = 0; i < segmentedControl.numberOfSegments; i++) {
        CGFloat width = [segmentedControl widthForSegmentAtIndex:i];
        if (width == 0.0f) {
            // auto sized
            numberOfAutosizedSegmentes++;
            [segmentWidths addObject:[NSNull null]];
        }
        else {
            // manually sized
            autosizedWidth -= width;
            [segmentWidths addObject:@(width)];
        }
    }

    CGFloat autoWidth = floorf(autosizedWidth/(float)numberOfAutosizedSegmentes);
    CGFloat realWidth = (segmentedControl.numberOfSegments-1);      // add all the 1pt. borders between the segments
    for (NSInteger i = 0; i < [segmentWidths count]; i++) {
        id width = segmentWidths[i];
        if (width == [NSNull null]) {
            realWidth += autoWidth;
        }
        else {
            realWidth += [width floatValue];
        }
    }

    CGRect whiteViewFrame = segmentedControl.frame;
    whiteViewFrame.size.width = realWidth;

    UIView *whiteView = [[UIView alloc] initWithFrame:whiteViewFrame];
    whiteView.backgroundColor = [UIColor whiteColor];
    whiteView.layer.cornerRadius = 5.0f;
    [self.view insertSubview:whiteView belowSubview:segmentedControl];
    return whiteView;
}

Please take care of frame changes yourself.

See this screenshot to see the difference between the two controls. All frames are 280 pt. wide.

Because of the formula UISegmentedControl uses the first controls actual size is 278 pt. And the real size of the second one is 279 pt.

enter image description here


The problem is that this somehow relies on the implementation of UISegmentedControl. Apple could for example change the implementation so segmentWidth that end in .5 points will be displayed. They could easily do this on retina displays.

If you use this code you should check your app on new iOS versions as early as possible. We are relying on implementation details, and those could change every day. Fortunately nothing bad happens if they change the implementation. It will just not look good.

like image 25
Matthias Bauch Avatar answered Nov 14 '22 19:11

Matthias Bauch


I know this is kind of a hack but you could just use a rounded UIView with white background placed just underneath - and aligned with - the segmented control, except for the width which should be equal to the original control's width minus 1.

Result: Segmented Control with White Background

like image 2
neural5torm Avatar answered Nov 14 '22 20:11

neural5torm