如何在iOS 13中将UISegmentedControl的背景色设置为白色

27
iOS 13对UISegmentedControl进行了一些更改,包括在切换选定段时引入了非常好的动画效果。但是我注意到它没有正确显示backgroundColor属性,它似乎总是有点色调。
我看到了回答如何设置selectedSegmentTintColor等内容的问题,但我很难将backgroundColor设置为.white,无论我做什么,它总是显示为灰色,即使没有应用tintColor或类似的设置。将backgroundColor设置为其他颜色会显示相同的行为,但白色最明显。更神秘的是,虽然这种差异在运行iOS 13的物理设备和模拟器上都显示出来,但视觉调试器(在XCode 11 GM2中)不显示此差异!
以下是一些屏幕截图,即使UISegmentedControl的backgroundColor设置为与其后面显示的视图的backgroundColor相同,它们也略有不同。
运行iOS 13的设备(白色backgroundColor) enter image description here 在Visual Debugger中显示的相同视图/代码(白色backgroundColor) enter image description here

设备运行iOS 13 (蓝色背景) enter image description here

我尝试了在这个SO帖子中建议的应用backgroundImage的建议: UISegmentedControl iOS 13 clear color,但这会使样式回到在iOS 12中的样子,而且你也会失去漂亮的动画。

非常感谢任何指导或建议!我也向苹果提交了错误报告,看看是否有什么结果。


你解决了这个问题吗?我也遇到了同样的问题。 - Carioni
很遗憾,我还没有。在发布这个问题几天后,我向苹果提交了一个错误报告,但是到目前为止,我还没有得到任何回复。 - Phil
我已经解决了我的问题,以下是解决方法。 - Carioni
7个回答

30

我有同样的问题,但没有很好的方法解决它。所以我做了一个小的变通方法。虽然我不喜欢它,也不为它感到骄傲,但它确实有效。

func fixBackgroundSegmentControl( _ segmentControl: UISegmentedControl){
    if #available(iOS 13.0, *) {
        //just to be sure it is full loaded
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 
            for i in 0...(segmentControl.numberOfSegments-1)  {
                let backgroundSegmentView = segmentControl.subviews[i]
                //it is not enogh changing the background color. It has some kind of shadow layer 
                backgroundSegmentView.isHidden = true 
            }
        }
    }
}

1
看看我下面的答案,我刚找到了一种比这个更酷的方法,即使你的方法也能工作。 - Carioni
这个方法很管用!我已经完全放弃了,感谢您分享您的解决方案。虽然有点hacky,但考虑到手头的荒谬情况,它感觉很合适 :) - Phil
3
用这段代码后,我的分段控件失去了文本,但灰色叠加仍然存在。 - IvanPavliuk
1
使用这个解决方案后,我的分段控件中的分隔符消失了。你有同样的副作用吗?如果没有,能否分享一下你的分段控件配置? - ste8
使用此解决方案后,我的分段控件之间的分隔符消失了。您是否遇到了相同的副作用?如果没有,能否分享一下您的分段控件配置? - Mallikarjun C
显示剩余2条评论

4

我找到了最简单的解决方案。

let segmentControl: UISegmentControl = ... 
segmentControl.subviews.forEach { subview in
  subview.backgroundColor = .white
}

1
很遗憾,使用这个解决方案会丢失所选的背景颜色。 - Thomas Mary
如何处理这个问题?有什么建议? - IvanovDeveloper
在这种解决方案之后,“selectedSegmentTintColor”无法正常工作。 - Argus
在SwiftUI中有没有一种方法可以实现“子视图”这个东西? - Max

2

这对我有用(Swift 5)。

let background = myColors.background
let selectedColor = myColors.foreground

if #available(iOS 13.0, *)
{
    segmentedControl.tintColor = background
    segmentedControl.backgroundColor = background
    segmentedControl.selectedSegmentTintColor = selectedColor
    segmentedControl.setTitleTextAttributes([.foregroundColor: selectedColor as Any], for: .normal)
    segmentedControl.setTitleTextAttributes([.foregroundColor: background as Any], for: .selected)
}
else
{
    segmentedControl.tintColor = background
    segmentedControl.backgroundColor = selectedColor
    segmentedControl.layer.cornerRadius = 4
}

9
除了 .white 和 .clear 之外,这种方法似乎适用于任何 backgroundColor。但是,当你将其设置为这两种颜色之一时,显示出来的是一种难看的灰色。 - c0d3p03t
1
@c0d3p03t 我认为灰色混合在任何浅色中都是可见的。我试图使用浅棕色,但我也看到它与UIColor.yellow一起出现。 - arlomedia
这是对我有效的答案。然而,在字典值中您不需要使用 as Any - NRitH

1

我完成了之前回答者的代码,一切都运行正常。

extension UISegmentedControl {

    func applyWhiteBackgroundColor() {
        // for remove bottom shadow of selected element
        self.selectedSegmentTintColor = selectedSegmentTintColor?.withAlphaComponent(0.99)
        if #available(iOS 13.0, *) {
            //just to be sure it is full loaded
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
                guard let self = self else {
                    return
                }
                for i in 0 ..< (self.numberOfSegments)  {
                    let backgroundSegmentView = self.subviews[i]
                    //it is not enogh changing the background color. It has some kind of shadow layer
                    backgroundSegmentView.isHidden = true
                }
            }
        }
    }
}

}

修改后的分段控制器


1
已接受的答案可以简化,我们可以通过子类化 UISegmentedControl 并重写 layoutSubviews 方法来避免使用 DispatchQueue.main.async 调用:
class SegmentedControl: UISegmentedControl {
  override func layoutSubviews() {
    super.layoutSubviews()
    for i in 0...(numberOfSegments - 1)  {
      subviews[i].isHidden = true
    }
  }
}

使用这个解决方案后,我的分段控件中的分隔符消失了。你有同样的副作用吗?如果没有,能否分享一下你的分段控件配置? - Mallikarjun C

1

SWIFT 3和4+

根据https://dev59.com/1lwZ5IYBdhLWcg3wROeN#31652184的答案,如果您想要一个全白背景而没有灰色覆盖层,只需将tintColorbackgroundColor替换为UIColor.white

extension UISegmentedControl {
    func removeBorders() {
        setBackgroundImage(imageWithColor(color: backgroundColor!), for: .normal, barMetrics: .default)
        setBackgroundImage(imageWithColor(color: tintColor!), for: .selected, barMetrics: .default)
        setDividerImage(imageWithColor(color: UIColor.clear), 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!
    }
}

1
嗨,Carioni,我之前尝试过这种方法,除非我漏掉了什么,否则在更改选择时会失去动画效果,而这正是我想要保持的。Erick Silva的建议虽然有点hacky,但确实可以解决问题。 - Phil
@Phil,你能分享一个视频示例吗?我不确定你指的是什么,因为我没有失去任何动画。 - Carioni

-1
在Xamarin.iOS中,这对我起作用:
class MySegmentedControl : UISegmentedControl
{
    int insertedIndex = 0;

    public override void InsertSubview(UIView view, nint atIndex)
    {
        base.InsertSubview(view, atIndex);

        if (insertedIndex == 2 || insertedIndex == 3)
            view.Hidden = true;

        insertedIndex++;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接