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

前端 未结 14 870
醉话见心
醉话见心 2020-11-30 18: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

14条回答
  •  醉话见心
    2020-11-30 18:27

    While answers above are great, most of them get the color of text inside selected segment wrong. I've created UISegmentedControl subclass which you can use on iOS 13 and pre-iOS 13 devices and use the tintColor property as you would on pre iOS 13 devices.

        class LegacySegmentedControl: UISegmentedControl {
            private func stylize() {
                if #available(iOS 13.0, *) {
                    selectedSegmentTintColor = tintColor
                    let tintColorImage = UIImage(color: tintColor)
                    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
    
    // Detect underlying backgroundColor so the text color will be properly matched
    
                    if let background = backgroundColor {
                        self.setTitleTextAttributes([.foregroundColor: background, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .selected)
                    } else {
                        func detectBackgroundColor(of view: UIView?) -> UIColor? {
                            guard let view = view else {
                                return nil
                            }
                            if let color = view.backgroundColor, color != .clear {
                                return color
                            }
                            return detectBackgroundColor(of: view.superview)
                        }
                        let textColor = detectBackgroundColor(of: self) ?? .black
    
                        self.setTitleTextAttributes([.foregroundColor: textColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .selected)
                    }
                }
            }
    
            override func tintColorDidChange() {
                super.tintColorDidChange()
                stylize()
            }
        }
    
        fileprivate extension UIImage {
            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)
            }
        }
    

    Using tintColorDidChange method we ensure that the stylize method will be called every time the tintColor property changes on the segment view, or any of the underlying views, which is preferred behaviour on iOS.

    Result:

提交回复
热议问题