I'm trying to customize the following segmented control, using a left image for the first button and a right image for the second button. How would I do this using UIAppearance?
I want to change the following segmentedControl:
to something similar like below:
The reason I want to use a custom image is so that I can change the corners of the buttons. If you look at the blue segmented control, it's more squared (my image has it's own corners).
I was thinking of something like this but no use:
UIImage *leftImage = [[UIImage imageNamed:@"leftControl.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
UIImage *rightImage = [[UIImage imageNamed:@"rightControl.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
[[UISegmentedControl appearance] setBackgroundImage:leftImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault ];
[[UISegmentedControl appearance] setBackgroundImage:rightImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
You need to provide the following images:
- segment background selected (this has both left and right caps)
- segment background unselected (this has both left and right caps)
- segment middle, left selected, right unselected
- segment middle, left unselected, right selected
- segment middle, both left & right selected
- segment middle, both left & right unselected
And then use the following code to set:
/* Unselected background */
UIImage *unselectedBackgroundImage = [[UIImage imageNamed:@"segment_background_unselected"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
[[UISegmentedControl appearance] setBackgroundImage:unselectedBackgroundImage
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
/* Selected background */
UIImage *selectedBackgroundImage = [[UIImage imageNamed:@"segment_background_selected"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
[[UISegmentedControl appearance] setBackgroundImage:selectedBackgroundImage
forState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
/* Image between two unselected segments */
UIImage *bothUnselectedImage = [[UIImage imageNamed:@"segment_middle_unselected"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearance] setDividerImage:bothUnselectedImage
forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
/* Image between segment selected on the left and unselected on the right */
UIImage *leftSelectedImage = [[UIImage imageNamed:@"segment_middle_left_selected"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearance] setDividerImage:leftSelectedImage
forLeftSegmentState:UIControlStateSelected
rightSegmentState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
/* Image between segment selected on the right and unselected on the left */
UIImage *rightSelectedImage = [[UIImage imageNamed:@"segment_middle_right_selected"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearance] setDividerImage:rightSelectedImage
forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
Note that you'll have to adjust the cap size in the stretchable images to match your images.
Maurizio's answer didn't quite work for me with a segmented control within a toolbar; it kept showing these phantom lines on the controls since the divider images weren't wide enough.
So I made my own. Here are all of the images you will need for Xcode and I also put in the Photoshop files for creating the segmented control images so you can change the styling:
https://s3.amazonaws.com/iwasrobbed/stackoverflow/Custom+Segmented+Control.zip
Put this in your AppDelegate to have it change the appearance, using the attached images, of all segmented controls within a toolbar:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self customizeAppAppearance];
}
- (void)customizeAppAppearance
{
// Toolbar
[[UIToolbar appearance] setBackgroundImage:[[UIImage imageNamed:@"toolbar.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(22, 5, 22, 5)] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
// Segmented Controls within Toolbars
// Unselected background
UIImage *unselectedBackgroundImage = [[UIImage imageNamed:@"segmentInactive.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 15, 15, 15)];
[[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] setBackgroundImage:unselectedBackgroundImage
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
// Selected background
UIImage *selectedBackgroundImage = [[UIImage imageNamed:@"segmentActive.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 15, 15, 15)];
[[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] setBackgroundImage:selectedBackgroundImage
forState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
// Image between two unselected segments
UIImage *bothUnselectedImage = [[UIImage imageNamed:@"segmentBothInactive.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 10, 15, 10)];
[[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] setDividerImage:bothUnselectedImage
forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
// Image between segment selected on the left and unselected on the right
UIImage *leftSelectedImage = [[UIImage imageNamed:@"segmentLeftActiveRightInactive.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] setDividerImage:leftSelectedImage
forLeftSegmentState:UIControlStateSelected
rightSegmentState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
// Image between segment selected on the right and unselected on the left
UIImage *rightSelectedImage = [[UIImage imageNamed:@"segmentRightActiveLeftInactive.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] setDividerImage:rightSelectedImage
forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
}
You need to make one background image that is for all your segments, and also an image that is just the left edge of a button, an image that is the join between two buttons, and an image that is the right edge. Some of those need to be done for multiple states. So here is your image list:
- left cap, selected
- left cap, unselected
- segment background, selected
- segment background, unselected
- right cap, selected
- right cap, unselected
- middle cap, left selected right unselected
- middle cap, left unselected, right selected
- middle cap, both selected
- middle cap, both unselected
For the middle caps you can put them in like this: (text from Apple docs).
// Image between two unselected segments.
[mySegmentedControl setDividerImage:image1 forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateNormal barMetrics:barMetrics];
// Image between segment selected on the left and unselected on the right.
[mySegmentedControl setDividerImage:image1 forLeftSegmentState:UIControlStateSelected
rightSegmentState:UIControlStateNormal barMetrics:barMetrics];
// Image between segment selected on the right and unselected on the right.
[mySegmentedControl setDividerImage:image1 forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateSelected barMetrics:barMetrics];
If you're using UIAppearance
, obviously you'd send those messages to the appearance proxy.