25

I have a UISegmentedControl in my app. As of iOS7 GM, the images I use are not showing up when run on iOS7 devices. Anyone else having this problem?

Here's what it looks like in iOS6.1 and earlieriOS6-ScreenShot .

and here is what it looks like in iOS7iOS7-ScreenShot .

Here is the code:

self.theSegmentedControl.frame = CGRectMake(self.theSegmentedControl.frame.origin.x, self.theSegmentedControl.frame.origin.y, 320, 35);
[self.theSegmentedControl setBackgroundImage:[UIImage imageNamed:@"img_toggleInactive"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self.theSegmentedControl setImage:[UIImage imageNamed:@"btn_onceActive"] forSegmentAtIndex:0];
[self.theSegmentedControl setImage:[UIImage imageNamed:@"btn_recurringInactive"] forSegmentAtIndex:1];
[self.theSegmentedControl setImage:[UIImage imageNamed:@"btn_scheduledInactive"] forSegmentAtIndex:2];
[self.theSegmentedControl setDividerImage:[UIImage imageNamed:@"separator"] forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

Has anyone found a workaround to this?

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
kevmalek
  • 1,373
  • 2
  • 12
  • 13
  • it's not a but, it's a feature! Apple made the very strange choice to colour entirely the images in his segmented controls. I my app I used a seg to choose a colour, now my colours are all blue! – Martin Nov 27 '13 at 07:45

4 Answers4

61

Woohoo! Here's the workaround:

//Add clear color to mask any bits of a selection state that the object might show around the images
self.theSegmentedControl.tintColor = [UIColor clearColor];

UIImage *onceActive;
UIImage *recurringActive;
UIImage *scheduledActive;
UIImage *separator;

//Setting imageWithRenderingMode: to imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal for iOS7 is key
if ([UIImage instancesRespondToSelector:@selector(imageWithRenderingMode:)]) {
    onceActive = [[UIImage imageNamed:@"btn_onceActive"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    recurringActive = [[UIImage imageNamed:@"btn_recurringInactive"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    scheduledActive = [[UIImage imageNamed:@"btn_scheduledInactive"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    separator = [[UIImage imageNamed:@"separator"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

}
else {
    onceActive = [UIImage imageNamed:@"btn_onceActive"];
    recurringActive = [UIImage imageNamed:@"btn_recurringInactive"];
    scheduledActive = [UIImage imageNamed:@"btn_scheduledInactive"];
    separator = [UIImage imageNamed:@"separator"];
}


[self.theSegmentedControl setImage:onceActive forSegmentAtIndex:0];
[self.theSegmentedControl setImage:recurringActive forSegmentAtIndex:1];
[self.theSegmentedControl setImage:scheduledActive forSegmentAtIndex:2];
[self.theSegmentedControl setDividerImage:separator forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
kevmalek
  • 1,373
  • 2
  • 12
  • 13
  • 2
    That's not the 'workaround' it was explicitly called out in one of the WWDC videos as a needed thing to do in that case – Anya Shenanigans Sep 11 '13 at 08:14
  • 25
    The code without the new method worked in iOS6.1 and lower and stopped in 7. There is no indication in the docs or console to do anything differently. The onus of responsibility doesn't lie on me to watch all the WWDC videos. I'm gonna go ahead and call this one a bug, thank you. – kevmalek Sep 11 '13 at 15:51
  • 2
    Did you recompile your code using the new SDK? The docs *clearly* state that: Segment Icons You can use an image instead of title text for your segments. Note that a segment image will be automatically rendered as a template image within a segmented control, unless you explicitly set its rendering mode to UIImageRenderingModeAlwaysOriginal. For more information, see Template Images (I'm not linking to it, as the link is still pre-release) – Anya Shenanigans Sep 11 '13 at 20:24
  • 1
    You're right, the behavior is mentioned in the transition guide, thank you. – kevmalek Sep 11 '13 at 22:55
  • I tried this but the image I set in not scalling to fill the segment. see http://stackoverflow.com/questions/18850081/ios7-images-inuisegmentedcontrol-not-scaling – user1028028 Sep 19 '13 at 14:38
  • I try this snippet in my app,it`s worked.so,what`s the conclusion,should I use this ? – newlife Sep 24 '13 at 08:09
  • @newlife Yes use this. This is not a workaround, it's rather the new behavior for the UISegmentedControl going forward in iOS7. – kevmalek Sep 24 '13 at 20:46
  • I think the only problem is that the default `renderingMode` should be `UIImageRenderingModeAlwaysOriginal` – chunkyguy Dec 05 '13 at 23:07
18

UPDATE for Xcode 6/iOS 8

Now you can do it in Interface builder

Just add the image file in the asset catalog and set its "render as" original image instead of default

Xcode 5

The new UISegmented control uses the tint color to tint the images using the template mode. You will need to render these images as original and not templates.

As suggested in the comments do this:

UIImage* onceActive = [UIImage imageNamed:@"btn_onceActive"];
if (IOS_7_MACRO)
    onceActive = [onceActive imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[self.theSegmentedControl setImage:onceActive forSegmentAtIndex:0];
João Nunes
  • 3,751
  • 31
  • 32
7

Use this Code to set Image on Segment Control in iOS 7 with xCode 5.0

if ([UIImage instancesRespondToSelector:@selector(imageWithRenderingMode:)]) {

    [segmentControl setImage:[[UIImage imageNamed:@"image.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forSegmentAtIndex:0];

}
else {
    [segmentControl setImage:[UIImage imageNamed:@"image.png"] forSegmentAtIndex:0];
}
Himanshu Mahajan
  • 4,779
  • 2
  • 36
  • 29
2

It could be useful to create a category:

@interface UISegmentedControl (UISegmentedControlAdditions)
    -(void)setImageRenderingMode:(UIImageRenderingMode)renderingMode;
@end    

@implementation UISegmentedControl (UISegmentedControlAdditions)
    -(void)setImageRenderingMode:(UIImageRenderingMode)renderingMode {
        for (int index=0; index < [self numberOfSegments]; index++) {
            UIImage * image = [self imageForSegmentAtIndex:index];
            [self setImage:[image imageWithRenderingMode:renderingMode] forSegmentAtIndex:index];
        }
    }

... and just call

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)
    [colorSegmentedControl setImageRenderingMode:UIImageRenderingModeAlwaysOriginal];
Martin
  • 11,881
  • 6
  • 64
  • 110
  • `imageWithRenderingMode:` is already present in `UIImage`. Then why create category? Only available in iOS 7.0 onwards. – Akshit Zaveri Mar 06 '14 at 13:04
  • Yes, `imageWithRenderingMode:` is also a `UIImage` method: this example is using it. Why create a category? Because it's less verbose and useful than configuring manually your controls. But of course, it's even better by creating your own `UISegmentedControl` class. – Martin Mar 06 '14 at 17:11