UIControlSegment background color is not transparent anymore on iOS13

825 Views Asked by At

I have a UISegmentedControl. it was working fine before iOS13 (I set both backgroundColor and tintColor to clear). But now, I don't get the same result. My SegmentedControl has a light grey layer. I made some research but nothing work. (I have the same problem as this: https://forums.developer.apple.com/thread/123955)

Previous state:

enter image description here

current state - iOS13:

enter image description here

5

There are 5 best solutions below

0
On BEST ANSWER

thanks @Maulik Pandya for the answer, I have improve your answer little bit.

extension UISegmentedControl {
  func clearBG() {
    let clearImage = UIImage().imageWithColor(color: .clear)
    setBackgroundImage(clearImage, for: .normal, barMetrics: .default)
    setBackgroundImage(clearImage, for: .selected, barMetrics: .default)
    setDividerImage(clearImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
  }
}

public extension UIImage {
  public func imageWithColor(color: UIColor) -> UIImage {
    let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
    UIGraphicsBeginImageContext(rect.size)
    guard let context = UIGraphicsGetCurrentContext() else { return UIImage()}
    context.setFillColor(color.cgColor)
    context.fill(rect)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
  }
}
0
On

I also have face this issue to meet the design requirement. In my case SegmentedControl background color should be white color or clear color because view's background color was also white.

So I just add a white UImage in SegmentControl background and this trick worked for me.

if (@available(iOS 13.0, *)) {
   [segmentController setBackgroundImage:[UIImage imageNamed:@"someWhiteImage"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
}

Although it is not a perfect solution but my requirement meets using this trick. You may also add a solid color background image in background for now. Hopefully someone show us the right way of handling.

Required Design and before iOS 13

enter image description here

After iOS 13

enter image description here

So adding white background image worked for me

0
On

.selectedSegmentTintColor defines the selected button color and .layer.backgroundColor the color for the whole UISegmentedControl background.

It turns out this won't work for background clear or white since on iOS 13 a sort of background image is added on the background and dividers of the segmented control.

A workaround is create an image from color with UIGraphicsGetImageFromCurrentImageContext. Try this:

class ViewController: UIViewController {

    @IBOutlet weak var segmentedControl: UISegmentedControl!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        segmentedControl.setiOS12Layout(tintColor: .red)
    }
}

extension UISegmentedControl {

    func setiOS12Layout(tintColor: UIColor) {

        if #available(iOS 13, *) {

            let background = UIImage(color: .clear, size: CGSize(width: 1, height: 32))
             let divider = UIImage(color: tintColor, size: CGSize(width: 1, height: 32))
             self.setBackgroundImage(background, for: .normal, barMetrics: .default)
             self.setBackgroundImage(divider, for: .selected, barMetrics: .default)
             self.setDividerImage(divider, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
             self.layer.borderWidth = 1
             self.layer.borderColor = tintColor.cgColor
             self.setTitleTextAttributes([.foregroundColor: tintColor], for: .normal)
             self.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)

        } else {

            self.tintColor = tintColor
        }
    }
}
extension UIImage {

    convenience init(color: UIColor, size: CGSize) {

        UIGraphicsBeginImageContextWithOptions(size, false, 1)
        color.set()
        let context = UIGraphicsGetCurrentContext()!
        context.fill(CGRect(origin: .zero, size: size))
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        self.init(data: image.pngData()!)!
    }
}

Result:

enter image description here

0
On

You can create an extension to make your segmentcontrol clear

segmentControl.clearBG()

Here is the extention

extension UISegmentedControl {
    func clearBG() {
        setBackgroundImage(imageWithColor(color: UIColor.white), for: .normal, barMetrics: .default)
        setBackgroundImage(imageWithColor(color: UIColor.white), for: .normal, barMetrics: .default)
        setDividerImage(imageWithColor(color: .white), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
    }

    // create a 1x1 image with this color
    private func imageWithColor(color: UIColor) -> UIImage {
        let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        context!.setFillColor(color.cgColor);
        context!.fill(rect);
        let image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image!
    }
}
2
On

For me the only way this worked out was to set UISegmentedControl's background image an empty image using UIImage(). Like so:

segmentedControl.setBackgroundImage(UIImage(), for: .normal, barMetrics: .default)

Also,

segmentedControl.setDividerImage(UIImage(), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)

if you want to get rid of the divider as well.