38

How do I make a custom UISegmentedControl?

I have 2 images, 1 that should be displayed when the segment is active and the other if the segment is inactive. Can i override the style or something so i have a UISegmentedControl with my own images as active/inactive background?

Ben Scheirman
  • 40,531
  • 21
  • 102
  • 137

10 Answers10

33

I had to add this extra code, in addition to having 2 different states for the "on" and "off" positions:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Set set segControl background to transparent
    CGRect rect = CGRectMake(0, 0, 1, 1);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
    CGContextFillRect(context, rect);
    UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    [self.segControl setBackgroundImage:transparentImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

    [self.segControl setDividerImage:transparentImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
}

EDIT: Because this is getting some publicity, a cleaner solution is to use [UIImage new] instead of creating transparent images, as such:

 [self.segControl setDividerImage:[UIImage new] forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
 [self.segControl setBackgroundImage:[UIImage new] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
Jon
  • 7,848
  • 1
  • 40
  • 41
  • This is great, fixes the last part (removed black background for me, my segment images were with holes), but, unfortunately, works only on ios5+. Didn't find any fast solution for 4 so I've replaced segmented control with several buttons. – MANIAK_dobrii Oct 30 '12 at 08:12
  • This will lose the rounded corners, any way to keep them? – MobileMon Jun 10 '13 at 16:05
  • This will also lose the border drawn around the segmented control. Any way to keep that? – MobileMon Jun 10 '13 at 16:27
15

You can use the methods which are described in the iOS developer libary:

http://developer.apple.com/library/ios/ipad/#documentation/uikit/reference/UISegmentedControl_Class/Reference/UISegmentedControl.html

Scroll down to the "Customizing Appearance" section. There are methods to set background images for several button states, button divider images, etc.

These methods are only available in iOS5 and later.

@property tintColor  
– backgroundImageForState:barMetrics:
– setBackgroundImage:forState:barMetrics:
– contentPositionAdjustmentForSegmentType:barMetrics:
– setContentPositionAdjustment:forSegmentType:barMetrics:
– dividerImageForLeftSegmentState:rightSegmentState:barMetrics:
– setDividerImage:forLeftSegmentState:rightSegmentState:barMetrics:
– titleTextAttributesForState:
– setTitleTextAttributes:forState:
Bocaxica
  • 3,911
  • 3
  • 37
  • 54
  • 1
    A great resource that explains how to use these can be found here: http://smnh.me/customizing-appearance-of-uisegmentedcontrol/ – ospr May 26 '16 at 01:19
9

The simplest way would be to create your own control that mimics UISegmentedControl. UISegmentedControl just arranges a series of buttons and manages their image states for you; it doesn't do anything special.

rpetrich
  • 32,196
  • 6
  • 66
  • 89
6

Yes, you you DO need 2 images (on and off) for each section of the segment bar. (4 segments... 8 images.) But it lets you set a total of 16 choices! (All with only consuming 1 row in your GUI.)

I got everything working EXCEPT... how do you hide the original segment bar graphics?

Can't set alpha to 0. (It will also hide your images.)

Can't set "tintClear" to "clear". (Not sure why it makes it black and white.)

Can't set it to "hidden"... nothing will work at all.

Can't set "background" to "clear". (The background is NOT the segmentbar graphics.)

Patricia
  • 309
  • 3
  • 11
6

I wrote something that works as @rpetrich was explaining without placing in a array and in my opinion is the easiest solution to this. Hope someone finds this useful

.h

IBOutlet UIButton *index0;
IBOutlet UIButton *index1;
IBOutlet UIButton *index2;
IBOutlet UIImageView *segMentControl;

-(IBAction)segmentSwitch:(UIButton *) buttonIndexPressed;

.m

-(IBAction)segmentSwitch:(UIButton *) buttonIndexPressed
{
    if (buttonIpressed == index0)
    {
        [segmentControl setImage:[UIImage imageNamed:@"Seg1Sel.png"]];
        NSLog(@"index 0 pushed");

        index0.enabled = NO;
        index1.enabled = YES;
        index2.enabled = YES;        
    }
    else if (buttonIpressed == index1)
    {
         [segmentControl setImage:[UIImage imageNamed:@"Seg2Sel.png"]];
         NSLog(@"index 1 pushed");

         index0.enabled = YES;
         index1.enabled = NO;
         index2.enabled = YES;
    }
    else if (buttonIpressed == index2)
    {
        [segmentControl setImage:[UIImage imageNamed:@"Seg3Sel.png"]];
        NSLog(@"index 2 pushed");

        index0.enabled = YES;
        index1.enabled = YES;
        index2.enabled = NO;
    }
}
FreeAppl3
  • 858
  • 1
  • 15
  • 32
2

it works just fine

[segmentControl setImage:[UIImage imageNamed:@"Rolenew.png"] forSegmentAtIndex:0];
virata
  • 1,882
  • 15
  • 22
1

Try HMSegmentedControl, it allows images and other settings too. Available at https://github.com/HeshamMegid/HMSegmentedControl

Display Name
  • 4,502
  • 2
  • 47
  • 63
0

In order to do this you must listen for the UIControlEventValueChanged and change the image yourself. You shouldn't need to subclass the UISegmentedControl - remember composition over inheritance is preferred!

groundhog
  • 4,770
  • 2
  • 24
  • 24
  • Yes i thought about that but it gives me 2 problems. 1. I see the UIsegmentedcontroll below my images. 2. If i do it this way i have to create for every single segment 2 images because a sement can only hold a title or an image. –  Aug 17 '09 at 08:40
0

Swift 3.0 Version of Jon's answer.

var transparentImage = UIGraphicsGetImageFromCurrentImageContext() as? UIImage
UIGraphicsEndImageContext()
segControl.setBackgroundImage(transparentImage, for: .normal, barMetrics: .default)
segControl.setDividerImage(transparentImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
Saranjith
  • 11,242
  • 5
  • 69
  • 122
0

Swift 4

This is how I managed it. Make a segmented control and insert segments using the images. Be sure to set one image as selected as this will be the initial selected index. Also set the backgroundColor and tintColor to clear so that segmented control will only show your image.

var newSegmentedControl: UISegmentedControl = {
        let segmentedControl = UISegmentedControl()

        segmentedControl.insertSegment(with: UIImage(named: "x_Selected.jpg"), at: 0, animated: true)
        segmentedControl.insertSegment(with: UIImage(named: "y_Normal.jpg"), at: 1, animated: true)

        segmentedControl.addTarget(self, action: #selector(segmentedControlChanged), for: .valueChanged)

        segmentedControl.selectedSegmentIndex = 0
        segmentedControl.backgroundColor = .clear
        segmentedControl.tintColor = .clear            

    }()

Function called when the value of segmented control changes.

@objc func segmentedControlChanged(sender: UISegmentedControl) {

    if sender.selectedSegmentIndex == 0 {
        sender.setImage(UIImage(named: "x_Selected.jpg"), forSegmentAt: sender.selectedSegmentIndex)
        sender.setImage(UIImage(named: "y_Normal.jpg"), forSegmentAt: 1)
    } else if sender.selectedSegmentIndex == 1 {
        sender.setImage(UIImage(named: "x_Normal.jpg"), forSegmentAt: 0)
        sender.setImage(UIImage(named: "y_Selected.jpg"), forSegmentAt: sender.selectedSegmentIndex)
    } 

}

Hope you find this useful :)

arvinq
  • 656
  • 6
  • 12