How to change the colors of a segment in a UISegmentedControl in iOS 13?

A UISegmentedControl has a new appearance in iOS 13 and existing code to alter the colors of the segmented control no longer work as they did.

Prior to iOS 13 you could set the tintColor and that would be used for the border around the segmented control, the lines between the segments, and the background color of the selected segment. Then you could change the color of the titles of each segment using the foreground color attribute with titleTextAttributes.

Under iOS 13, the tintColor does nothing. You can set the segmented control's backgroundColor to change the overall color of the segmented control. But I can't find any way to alter the color used as the background of the selected segment. Setting the text attributes still works. I even tried setting the background color of the title but that only affects the background of the title, not the rest of the selected segment's background color.

In short, how do you modify the background color of the currently selected segment of a UISegmentedControl in iOS 13? Is there a proper solution, using public APIs, that doesn't require digging into the private subview structure?

There are no new properties in iOS 13 for UISegmentedControl or UIControl and none of the changes in UIView are relevant.


As of iOS 13b3, there is now a selectedSegmentTintColor on UISegmentedControl.

To change the overall color of the segmented control use its backgroundColor.

To change the color of the selected segment use selectedSegmentTintColor.

To change the color/font of the unselected segment titles, use setTitleTextAttributes with a state of .normal/UIControlStateNormal.

To change the color/font of the selected segment titles, use setTitleTextAttributes with a state of .selected/UIControlStateSelected.

If you create a segmented control with images, if the images are created as template images, then the segmented control's tintColor will be used to color the images. But this has a problem. If you set the tintColor to the same color as selectedSegmentTintColor then the image won't be visible in the selected segment. If you set the tintColor to the same color as backgroundColor, then the images on the unselected segments won't be visible. This means your segmented control with images must use 3 different colors for everything to be visible. Or you can use non-template images and not set the tintColor.

Under iOS 12 or earlier, simply set the segmented control's tintColor or rely on the app's overall tint color.


As of Xcode 11 beta 3

There is now the selectedSegmentTintColor property on UISegmentedControl.

See rmaddy's answer


To get back iOS 12 appearance

I wasn't able to tint the color of the selected segment, hopefully it will be fixed in an upcoming beta.

Setting the background image of the selected state doesn't work without setting the background image of the normal state (which removes all the iOS 13 styling)

But I was able to get it back to the iOS 12 appearance (or near enough, I wasn't able to return the corner radius to its smaller size).

It's not ideal, but a bright white segmented control looks a bit out of place in our app.

(Didn't realise UIImage(color:) was an extension method in our codebase. But the code to implement it is around the web)

extension UISegmentedControl {
    /// Tint color doesn't have any effect on iOS 13.
    func ensureiOS12Style() {
        if #available(iOS 13, *) {
            let tintColorImage = UIImage(color: tintColor)
            // Must set the background image for normal to something (even clear) else the rest won't work
            setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
            setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
            setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
            setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
            setTitleTextAttributes([.foregroundColor: tintColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
            setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
            layer.borderWidth = 1
            layer.borderColor = tintColor.cgColor
        }
    }
}

Image showing the effect of the above code