UISegmentedControl in UINavigationBar has wrong color

I have several screens that are in UINavigationControllers and have a UISegmentedControl as the title NavigationItem. On most of the UISegmentedControls, the text/image shown is tinted black instead of white as I have defined in both the Storyboard as well as in viewWillAppear.



iOS12

https://www.icloud.com/iclouddrive/0ybyt4UiuALTLsKa6HP-vGEyw#ios12


iOS13

https://www.icloud.com/iclouddrive/05lJm5MjA3qZOjwGQ7Kvbn3uw#ios13


At first I thought this was an iOS13 change that was made, but there are a couple of screens where the UISegmentedControl IS tinted white. I've looked at both the black and the white tinted screens in Storyboard as well as code and I can't seem to find any difference.


Any help with this would be greatly appreciated.


Thanks!

The appearance of segmented controls has changed considerably in iOS 13. To see some examples, open the Apple Calendar app on iPad and you can see a Day/Week/Month/Year segmented control in the navigation bar (the iPhone version has a different UI). Or click on the Info button in the Maps app to bring up Maps Settings to see another example. Or create a quick test project with a segmented control in it. Experiment with putting different background colours behind it.


By default on iOS 13, the segmented control has a white "bubble" behind the selected text/image. In dark mode that changes to medium grey. It seems that the background of the entire segment is slightly transparent, and your dark blue in the navigation bar doesn't fit well. If you look really closely at your iOS 13 image you can see a slightly different colour where the segmented control's background alters the blue.


I quickly created a test project and here's one possible solution. You would do this in code inside an #if available(iOS 13.0, *) block so that it only affects iOS 13.


        segmentedControl.backgroundColor = .white
        segmentedControl.selectedSegmentTintColor = blue
        segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .normal)
        segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)


This sets the background colour of the entire segment to white to try and provide good contrast against the blue background of the navigation bar. There is some segmented control's own partially transparent view affects this colour, so it appears as light gray, not white. I'm not sure if that can be changed. I think the colour will be very close to the grey below your navigation bar, so could fit well.


The selected segment bubble has the same blue as the navigation bar.


The color of the text then needs changing so that it can be read against the blue or white background: black for .normal (which affects non-selected segments) and white for .selected (the selected segment).


See https://www.icloud.com/iclouddrive/0KGu6wObZOG1_tUIa36DcHbnQ#segment


Depending on how/if you're handling dark mode you might need to use dynamic colours instead of the blue/black/white. This is just a suggestion, of course, but it seems to fit in with your design. If nothing else, it gives you an idea of the kinds of configuration that can be done.


This stack overflow answer says pretty much the same thing and also explains how images in segments are still tinted, but text is not any more: https://stackoverflow.com/questions/56436559/how-to-change-the-colors-of-a-segment-in-a-uisegmentedcontrol-in-ios-13/56874473#56874473


I wouldn't recommend any solution that relies on private implementation of the views inside the segmented control. There's an older answer (before the selectedSegmentTintColor was added) that can almost re-create the iOS 12 style. If you're desperate, you could try that.

But how can you actually get the backround to be white?? there seems to be a default image that is causing the backround to be a paly gray...

SegmentedControl background color

  1. Create a custom subclass of UISegmentedControl
  • (if you don't want to see the divider imageView between the segments):
final class SegmentedControl: UISegmentedControl {
     override func layoutSubviews() {
         super.layoutSubviews()
         (0..<numberOfSegments).forEach { index in
             if let subview = subviews[index] as? UIImageView {
                 subview.isHidden = true
             }
         }
     }
}
  • (if you want to see the divider imageView between the segments):
 final class SegmentedControl: UISegmentedControl {
     override func draw(_ rect: CGRect) {
         for index in 0..<numberOfSegments {
             let view = UIView(frame: subviews[index].bounds)
             view.backgroundColor = .white
             subviews[index].addSubview(view)
         }
     }
 }
  1. Create an instance of SegmentedControl and don't forget to set selectedSegmentTintColor, segmentedControl.backgroundColor = .white

UISegmentedControl in UINavigationBar has wrong color
 
 
Q