Search code examples
iosswiftios13uisegmentedcontrol

Match UISegmentedControl's color with that of translucent navigation bar


I am adding a segmented control directly under the (translucent) navigation bar, and want it to have the same color. I am trying to make it look like it's part of the navigation bar.

isOpaque = false
layer.opacity = 0.85
let bkImg = UIImage(color: .systemBackground.withAplhaComponent(0.85), size: CGSize(width: 1, height: 16))
segmentedCtrl.setBackgroundImage(bkImg, for: .normal, barMetrics: .default)
setDividerImage(bkImg, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)

I've tried playing with the opacity of the segmented control, and the color of bkImg, but that has no effect at all. It would have been quicker to just write a custom tab bar from scratch - the segmented control in iOS 13 is impossible to customize.


Solution

  • This is a total hack, but it looks good.

    enter image description here

    class CustomSegmentedControl: UISegmentedControl {
        private var underline: UIView!
    
        init() {
            super.init(frame: .zero)
    
            underline = UIView()
            underline.backgroundColor = tintColor
            view.addSubvie(underline)
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            // Set background color to match navigation bar
            let bgColor = UIColor.systemBackground.hsbComponents.brightness == 1.0
                ? UIColor.black.withAlphaComponent(0.028) // light
                : UIColor.white.withAlphaComponent(0.073) // dark
            let img = UIImage(color: bgColor, size: CGSize(width: 1, height: 16))
            setBackgroundImage(img, for: .normal, barMetrics: .default)
            setDividerImage(img, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
    
            // Bonus: underline below selected tab
            var frame = underline.frame
            let tabWidth = self.frame.width / CGFloat(numberOfSegments)
            frame.origin.x = CGFloat(selectedSegmentIndex) * tabWidth
            frame.origin.y = self.frame.height - 2.0
            frame.size.width = tabWidth
            underline.frame = frame
        }
    }