How to change the corner radius of UISegmentedControl?

43,040

Solution 1

This should work:

self.segmentedControl.layer.cornerRadius = 15.0;
self.segmentedControl.layer.borderColor = [UIColor whiteColor].CGColor;
self.segmentedControl.layer.borderWidth = 1.0f;
self.segmentedControl.layer.masksToBounds = YES;

You need to specify the border after setting cornerRadius.

Solution 2

Embed UISegmentedControl inside UIView and set corner radius for UIView.

Objective-C

outerView.layer.cornerRadius = CGRectGetHeight(outerView.bounds) / 2;
outerView.layer.borderColor = [UIColor blueColor].CGColor;
outerView.layer.borderWidth = 1;

Swift

outerView.layer.cornerRadius = CGRectGetHeight(outerView.bounds) / 2
outerView.layer.borderColor = UIColor.blueColor().CGColor
outerView.layer.borderWidth = 1

Cornered UISegmentedControl

Solution 3

iOS 13 / 14 - FINALLY WORKING!!!

image alt

I decided to give it another go, and I finally got it working perfectly!! There's one line of code that fixed it all! Check out the code

Code:

class CustomSegmentedControl: UISegmentedControl{
    private let segmentInset: CGFloat = 5       //your inset amount
    private let segmentImage: UIImage? = UIImage(color: UIColor.red)    //your color

    override func layoutSubviews(){
        super.layoutSubviews()

        //background
        layer.cornerRadius = bounds.height/2
        //foreground
        let foregroundIndex = numberOfSegments
        if subviews.indices.contains(foregroundIndex), let foregroundImageView = subviews[foregroundIndex] as? UIImageView
        {
            foregroundImageView.bounds = foregroundImageView.bounds.insetBy(dx: segmentInset, dy: segmentInset)
            foregroundImageView.image = segmentImage    //substitute with our own colored image
            foregroundImageView.layer.removeAnimation(forKey: "SelectionBounds")    //this removes the weird scaling animation!
            foregroundImageView.layer.masksToBounds = true
            foregroundImageView.layer.cornerRadius = foregroundImageView.bounds.height/2
        }
    }
}

extension UIImage{
    
    //creates a UIImage given a UIColor
    public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
        let rect = CGRect(origin: .zero, size: size)
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
        color.setFill()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    
        guard let cgImage = image?.cgImage else { return nil }
        self.init(cgImage: cgImage)
    }
}

The idea is that Apple uses a UIImageView with its own square image and tint for the selected moving segment. What we want to do is instead overwrite its image so we have control on the color, corner radius, etc. After that, we want to remove one of Apple's 3 default animations (the one that was problematic made the segment scale up on touch -- I used foregroundImageView.layer.animationKeys() to find out what animations were affecting the moving segment)

Solution 4

The segmented control is not going to change the way it draws its corners, so it is continuing to draw its corners in its own way and you are then cutting them off. You are not in charge of how a segmented control draws its boundary shape. If you truly don't like the way it's drawn, you'll have to devise your own substitute control from scratch. The closest you can legitimately come to the kind of thing you're trying to do is to set the segmented control's background image.

Solution 5

You're able to change the UISegmentedControl cornerRadius by increasing the layer its cornerRadius or from iOS 13 and above by setting the layer its maskedCorner property.

This example removes the default cornerRadius and straightens the backgroundImage:

if #available(iOS 13.0, *) {
    segmentedControl.layer.maskedCorners = .init()
} else {
    segmentedControl.layer.cornerRadius = 0
}

Source: https://developer.apple.com/documentation/quartzcore/calayer/2877488-maskedcorners

Share:
43,040
wz366
Author by

wz366

In the Cloud. Worked at AWS, currently AZURE.

Updated on July 09, 2022

Comments

  • wz366
    wz366 almost 2 years

    Is it possible to change the corner radius of UISegmentedControl? I have tried the following approach which we use to change a UIView's corner radius.

    self.segmentedControl.layer.cornerRadius = 15.0;
    self.segmentedControl.layer.masksToBounds = YES;
    

    This did not work as you can see it only cuts off the UISegmentedControl's corner:

    cut off corner

  • Julian
    Julian over 9 years
    how does it change corner radius?
  • Mihai Timar
    Mihai Timar over 9 years
    This doesn't change the corner radius. It just supplements the original code posted in the question to get the expected result.
  • swathy krishnan
    swathy krishnan almost 9 years
    This will usually work . Provide your code so that i can look into it !
  • Axel Guilmin
    Axel Guilmin about 8 years
    It works only if you want to increase the cornerRadius
  • Yakiv Kovalskyi
    Yakiv Kovalskyi about 8 years
    It doesn't fix the initial problem with only corners being cut off. See my answer below.
  • Ilario
    Ilario over 7 years
    and don't forget to add Clip subviews to UIView in storyboard
  • swathy krishnan
    swathy krishnan about 7 years
    Check with some value for corner radius- segmentContrl.layer.cornerRadius = 3.0;
  • mondousage
    mondousage over 6 years
    For Swift: self.layer.cornerRadius = 15.0; self.layer.borderColor = UIColor.white.cgColor; self.layer.borderWidth = 1.0; self.layer.masksToBounds = true;
  • Booharin
    Booharin over 4 years
    I will pass it to designers:)
  • Merricat
    Merricat over 4 years
    hey, do you have any idea how to change the corner radius of the selectedSegment view?
  • mfaani
    mfaani over 4 years
    I don't know. I wouldn't recommend it either. With the right amount of engineering time you can create any layout, but is it worth the time? I would lean the UX desinger towards UX that Apple has built-in itself and avoid micro customazation
  • xi.lin
    xi.lin about 4 years
    @Manish How do you add the UISegmentedControl? I've tried in iOS 13 and it works well.
  • Sorin Dolha
    Sorin Dolha about 4 years
    This is gold, thank you for sharing. Note: you can move to layoutSubviews other setters too, to get them properly applied. I needed borderWidth myself.
  • xi.lin
    xi.lin about 4 years
    @user2185354 Could you upload a demo?
  • Dani
    Dani about 4 years
    @Ilario please explain
  • Lloyd Keijzer
    Lloyd Keijzer about 4 years
    @RaimondoPrevidi check my answer for editing the corner radius from iOS 13 and above by setting the CALayer its maskedCorners property stackoverflow.com/a/60651341/5324541
  • Lloyd Keijzer
    Lloyd Keijzer about 4 years
    @Manish check my answer for editing the corner radius from iOS 13 and above by setting the CALayer its maskedCorners property stackoverflow.com/a/60651341/5324541
  • Merricat
    Merricat about 4 years
    Thanks, but that does not affect the corner radius of the "moving" segment, only the background ones :( Edit: I finally found a way to access the "moving" view in the hierarchy with let movingSegment = subviews[numberOfSegments] as? UIimageView (basically it's the segments' backgrounds, then the moving one, then the foregrounds (i.e. each segment's title), so the "moving" view is always in the middle). Then I set the Image and highlightedImage to and empty UIImage(), inset the imageView's bounds by 5, and set the backgroundColor to the desired color.
  • Merricat
    Merricat about 4 years
    It looks exactly as it should, the problem is that during the tap animation the view grows as large as the segmentedControl, then shrinks again, so it looks wrong. Note that I'm doing these changes in the segmentedControl's layoutSubviews(). Look at my answer below to see my current results.
  • Lloyd Keijzer
    Lloyd Keijzer about 4 years
    @RaimondoPrevidi If u set a custom background image for the selected state is it still changing it's corner radius when moving?
  • Merricat
    Merricat about 4 years
    The problem is that I want the title, not an image. And on the docs it says you can only have one or the other.
  • Bishal Ghimire
    Bishal Ghimire almost 4 years
    The round rect is working. But how did you change the background color of selected index. In my case is set for both active and inactive case.
  • Merricat
    Merricat almost 4 years
    If I remember correctly, it's the foregroundImageView.backgroundColor = UIColor.darkGray line
  • Vishwanath Deshmukh
    Vishwanath Deshmukh almost 4 years
    Hi @RaimondoPrevidi Do you got any solution for that changing frame?
  • Merricat
    Merricat almost 4 years
    Unfortunately no. I just used what apple gave us, sacrificing the rounded corners. For me, it was not worth investing more hours to try and break apple's code.
  • GR1995
    GR1995 almost 4 years
    Take a look at this solution. kenb.us/… class SegmentedControlAnimationRemover { static var shared = SegmentedControlAnimationRemover() @objc func removeAnimation(_ control: UISegmentedControl) { control.layer.sublayers?.forEach { $0.removeAllAnimations() } } } segmentedControl.addTarget(SegmentedControlAnimationRemover.‌​shared, action: #selector(SegmentedControlAnimationRemover.removeAnimation(_‌​:)), for: .valueChanged)
  • Merricat
    Merricat almost 4 years
    I tried removing animations but it wasn’t changing anything. I’ll have to try this, maybe calling it on valueChanged might work.
  • Merricat
    Merricat almost 4 years
    @GR1995 So, right now I could only test this on a simulator, and it seems to work MUCH better, however I'm pretty sure I'm still noticing a small "growing" animation. I will test it on a device as soon as I can. I got frame drops from the simulator.
  • Merricat
    Merricat almost 4 years
    @GR1995 Ok so I tried it on my device and it doesn't change much. The weird animation between switching segments is definitely gone (there's still a bit of grow-shrink animation, but I think that's on me, probably due to changing its bounds in layoutSubviews()). Regardless, it still does its full awkward animation when tapping the currently selected segment (grows-shrinks a lot, in place). I tried with .allEvents but no changes. Also, for some reason, adding this code completely removed the sliding animation on my first segmented control, and only the first time I change its value. lol weird
  • Merricat
    Merricat over 3 years
    No, I let this one go for the time being. I'm soon upgrading the project to iOS 14, so I'll try this again and see if it's now doable
  • anoop4real
    anoop4real over 3 years
    @Merricat I managed to achieve some radius stuff (ofcourse without animation) see my Gist: gist.github.com/anoop4real/deffa9bbed3741b6c3f19c07d8c87502 ... following this thread stackoverflow.com/questions/58315497/… + some tweaks
  • Merricat
    Merricat over 3 years
    Currently not at home and can't try this... I got the round corners to work in my code above, but the problem was the weird animations when interacting with the segmented control! Does your code still have this animation issue?
  • Anil Kumar
    Anil Kumar almost 3 years
    @Merricat, excellent solution. Did you tried to remove background light grey color
  • Merricat
    Merricat almost 3 years
    @AnilKumar Thanks! No, I didn't need to remove the grey background, so I never tried :)
  • Oscar Fernandez
    Oscar Fernandez over 2 years
    Easy! Many thanks.
  • Malcolm Murray
    Malcolm Murray almost 2 years
    This answer works well for me. Should be the accepted answer.