SelectedTintColor of Segment Control is not rounded corner on iOS 13

Viewed 6354

Rounded corner is working great on iOS 12 and below, but it's broken on iOS 13. I've created a custom Segment control class.

Code:

class SegmentedControl: UISegmentedControl {
    override func layoutSubviews() {
      super.layoutSubviews()
      layer.cornerRadius = self.bounds.size.height / 2.0
      layer.borderColor = UIColor(red: 170.0/255.0, green: 170.0/255.0, blue: 170.0/255.0, alpha: 1.0).cgColor
      layer.borderWidth = 1.0
      layer.masksToBounds = true
      clipsToBounds = true

   }
}

I've gone through this post - How to change the colors of a segment in a UISegmentedControl in iOS 13? but I couldn't get any solution.

Screenshot: enter image description here

5 Answers

I was facing the same issue on iOS 13. Then I dug into its view hierarchy then I found it has multiple subviews. So I made a trick for iOS 13. You have to do following changes for iOS 13 -

  1. ChangeselectedSegmentTintColor to Clear - self.selectedSegmentTintColor = .clear
  2. Add following code snippet inside layoutSubviews -

    for i in 0...subviews.count - 1{
    
            if let subview = subviews[i] as? UIImageView{
    
                if i == self.selectedSegmentIndex {
    
                    subview.backgroundColor = UIColor(red: 170.0/255.0, green: 170.0/255.0, blue: 170.0/255.0, alpha: 1.0)
    
                }else{
    
                    subview.backgroundColor = .clear
                }
    
            }
        }
    

I hope it will help you.

Similar to other solution I have Following Sub-Class Segment control UISegmentedControl

Which gives following result -

enter image description here

class OYSegmentControl: UISegmentedControl {
  
  override func layoutSubviews(){
    super.layoutSubviews()
    
    let segmentStringSelected: [NSAttributedString.Key : Any] = [
      NSAttributedString.Key.font : UIFont.fontActionLabel(ofSize: 14.0),
      NSAttributedString.Key.foregroundColor : UIColor.white
    ]
    
    let segmentStringHighlited: [NSAttributedString.Key : Any] = [
      NSAttributedString.Key.font : UIFont.fontActionLabel(ofSize: 14.0),
      NSAttributedString.Key.foregroundColor : #colorLiteral(red: 0.5567105412, green: 0.5807551742, blue: 0.6022000909, alpha: 1)
    ]
    
    setTitleTextAttributes(segmentStringHighlited, for: .normal)
    setTitleTextAttributes(segmentStringSelected, for: .selected)
    setTitleTextAttributes(segmentStringHighlited, for: .highlighted)
    
    layer.masksToBounds = true
    
    if #available(iOS 13.0, *) {
      selectedSegmentTintColor = #colorLiteral(red: 0, green: 0.861200273, blue: 0.67304039, alpha: 1)
    } else {
      tintColor = #colorLiteral(red: 0, green: 0.861200273, blue: 0.67304039, alpha: 1)
    }
    
    backgroundColor = #colorLiteral(red: 0.9191747308, green: 0.9334954619, blue: 0.9506797194, alpha: 1)
    
    //corner radius
    let cornerRadius = bounds.height / 2
    let maskedCorners: CACornerMask = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
    //background
    clipsToBounds = true
    layer.cornerRadius = cornerRadius
    layer.maskedCorners = maskedCorners

    let foregroundIndex = numberOfSegments
    if subviews.indices.contains(foregroundIndex),
      let foregroundImageView = subviews[foregroundIndex] as? UIImageView {
      foregroundImageView.image = UIImage()
      foregroundImageView.clipsToBounds = true
      foregroundImageView.layer.masksToBounds = true
      foregroundImageView.backgroundColor = #colorLiteral(red: 0, green: 0.861200273, blue: 0.67304039, alpha: 1)
      
      foregroundImageView.layer.cornerRadius = bounds.height / 2 + 5
      foregroundImageView.layer.maskedCorners = maskedCorners
    }
  }
  
  override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return false
  }
  
}

Language: Swift 5.1

NOTE: This only works if you have outlet / frame set from Storyboard. Frame from code will cause issues. The extra 5 px on cornerRadius,a hack to make it better round rect. I ended up using - https://github.com/alokc83/MASegmentedControl as my use-case was from Code only view.

Swift 5

If you use a subclass:

override func layoutSubviews() {
    super.layoutSubviews()
    roundCorners(radius: frame.height / 2)

    if #available(iOS 13.0, *) {
        selectedSegmentTintColor = .clear
    } else {
        tintColor = .clear
    }

    for (index, subview) in subviews.enumerated() {
        if ((subviews[index] as? UIImageView) != nil) && index == selectedSegmentIndex {
            subview.backgroundColor = .white
            subview.roundCorners(radius: subview.frame.height / 2)
        } else {
            subview.backgroundColor = .clear
        }
    }
}

Conienience method:

extension UIView {
    func roundCorners(radius: CGFloat) {
        layer.roundCorners(radius: radius)
        self.clipsToBounds = true
    }
}

If you use the default segmented control, you just prefix with name of your segmented control:

mySegmentedControl.selectedSegmentTintColor = .clear

for (index, subview) in mySegmentedControl.subviews.enumerated() {
   .....
}

Make a custom class for segment

class CustomSegmentedControl: UISegmentedControl {
override func layoutSubviews() {
    super.layoutSubviews()
    layer.cornerRadius = self.bounds.size.height / 2.0
    layer.borderColor = use_your_custom_color
    layer.borderWidth = 1.0
    layer.masksToBounds = true
    clipsToBounds = true
    for i in 0...subviews.count - 1{
        if let subview = subviews[i] as? UIImageView{
            if i == self.selectedSegmentIndex {
                subview.backgroundColor = use_your_custom_color
            }else{
                subview.backgroundColor = .white
            }
        }
    }
}}

May be this will easy to use like this

   @IBOutlet weak var reminderSegmentControl: CustomSegmentedControl!

this code works for me iOS 13 - Swift 5.1

    segment.layer.cornerRadius = 12
    segment.layer.borderWidth = 1
    segment.layer.borderColor = UIColor.black.cgColor
    segment.font(name: "TheSans-Plain", size: 14)
    segment.clipsToBounds = true
    segment.layer.masksToBounds = true

    if #available(iOS 13.0, *) {
        segment.selectedSegmentTintColor = .red
    } 
Related