如何为单独的分段控件设置UISegmentedControl色调颜色

8

我开始学习Swift,并尝试转换这段Objective-C代码:

[[mySegmentedControl.subviews objectAtIndex:0] setTintColor:[UIColor blueColor]]

这段代码正确设置了第一个段的色调颜色。


这是我编写的最接近Swift版本的代码:

mySegmentedControl?.subviews[0].tintColor = UIColor.blueColor()

我收到的错误信息是'@Ivalue $T9' 与 'UIColor!!' 不一致
我不理解这个错误的含义。当我看 .tintColor 方法时,它列出了UIColor!?,而我还没有找到在Swift中!?表示什么意思。

如果mySegmentedControl是IBOutlet吗? 如果是的话,声明@IBOutlet var mySegmentedControl = UISegmentedControl!然后你就不需要?运算符了。这并不能解决整个问题,但这是一件事情。如果subviews [0]可能返回nil,则它是可选的,因此可以在[0]之后放一个!。 - clearlight
10个回答

23

这将解决您的问题:

var subViewOfSegment: UIView = mySegmentedControl.subviews[0] as UIView
subViewOfSegment.tintColor = UIColor.blueColor()

你也可以

(mySegmentedControl.subviews[0] as UIView).tintColor = UIColor .blueColor()

这个有文档记录吗? - igrek
我在每次点击片段时更改色调颜色,但它反映出错误的颜色,请帮忙!!! - Sujit Baranwal
有没有办法为突出显示的文本颜色设置不同的颜色?每个部分都有不同的颜色。 - tapizquent

13

我发现最简单的方法是:

segmentControl.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.redColor()], forState: UIControlState.Selected)

1
这对于简单的情况可行,如果您想根据选择更改按钮颜色。我需要更改特定索引颜色,而不管自定义UI的选择状态。 - Automate This
Swift 5 版本:segmentControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: UIControl.State.selected) - Mikrasya

5
这段代码适用于最新的Swift版本,即2019年8月份的Swift 3.0版本。
这里我实现了一个扩展程序,它适用于应用程序中的所有分段控件,在应用程序类中必须定义一组代码。
扩展方法可以直接在应用程序中使用,您也可以将所有设置添加到扩展类中的同一个方法或不同方法中,如下所示。
extension UISegmentedControl {
func setSegmentStyle() {
    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)



     let segAttributes: NSDictionary = [
            NSForegroundColorAttributeName: UIColor.gray,
            NSFontAttributeName: UIFont(name: "System-System", size: 14)!
        ]

        setTitleTextAttributes(segAttributes as [NSObject : AnyObject], for: UIControlState.selected)
    }

    // 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!
    }
}

以下代码段可用于各个部分

 self.mySegment.setSegmentStyle()

enter image description here


1
这在我看来是唯一合法的答案。这正是分段控件具有 setBackgroundImage(_ backgroundImage: UIImage?, for state: UIControl.State, barMetrics: UIBarMetrics) 的原因。所有其他解决方案都依赖于某个视图层次结构,而这可能会随着 iOS 版本的更改而变化。从 iOS 13 开始,使用 selectedSegmentTintColor: UIColor? 将变得更加容易。 - Joachim Deelen

2

更改所选分段的tintColor

使用UISegmentControl的value changed事件,按其原点x值对分段进行排序,然后循环并比较selectedSegmentIndex属性。以下是一个假设具有4个分段的分段控件的示例:

@IBAction func indexChanged(sender: UISegmentedControl) {

    let sortedViews = sender.subviews.sort( { $0.frame.origin.x < $1.frame.origin.x } )

    for (index, view) in sortedViews.enumerate() {
        if index == sender.selectedSegmentIndex {
            view.tintColor = UIColor.blueColor()
        } else {
            view.tintColor = UIColor.lightGrayColor()
        }
    }

}

在viewDidLoad中设置初始选定段的tintColor,本例中为第一个:

let sortedViews = segmentedControlOutletVariable.subviews.sort( { $0.frame.origin.x < $1.frame.origin.x } )
sortedViews[0].tintColor = UIColor.blueColor()

但是在排序后,索引与段索引不同。 - Bagusflyer

2

对于Swift 5.1,我发现以下内容可行:

//To set Text Colour when Segment Selected
segmentOutlet.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: UIControl.State.selected)

//To Set Background Colour when Segment Selected,
//The number in the [] is the segment that gets value change
let subViewOfSegment: UIView = segmentOutlet.subviews[1] as UIView
        subViewOfSegment.backgroundColor = UIColor.blue

我将这些放在Switch语句中,用于捕获按下按钮时的操作。


1

经过分析和尝试了许多答案,我意识到使用第三方自定义分段控件比尝试黑苹果的UISegmentedControl进行自定义更容易且更安全。

以下是使用XMSegmentedControl(Swift 3)进行自定义的示例。

部分代码:

    mySegmentControl.delegate = self
    mySegmentControl.font = UIFont.systemFont(ofSize: 12)

还有一些是通过Interface Builder来完成的(如果需要也可以通过代码完成):

enter image description here

结果是:

这里是结果。

enter image description here

在我的情况下,它看起来非常像系统一,但仍然有一些小差异,我必须完全按照设计师的要求去做。
请注意,XMSegmentedControl不允许为不同的分段设置不同的背景颜色,但是如果需要,您可以很容易地添加此功能,因为它是一个简单的.swift文件,非常容易理解和修改。

我已经更新了我的答案,展示了自定义的可能性。自定义分段控件具有简单明了的代码 - 您可以轻松修改它,以便能够为每个分段设置单独的颜色。 - Vitalii

0

sender.subviews.sort 在 Swift 4 中不起作用,而移除边框则参考 如何从分段控件中移除边框

extension UISegmentedControl {

// 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!
}

func removeBackgroundColors() {
    self.setBackgroundImage(imageWithColor(color: .clear), for: .normal, barMetrics: .default)
    self.setBackgroundImage(imageWithColor(color: .clear), for: .selected, barMetrics: .default)
    self.setDividerImage(imageWithColor(color: UIColor.clear), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
}

struct viewPosition {
    let originX: CGFloat
    let originIndex: Int
}

func updateTintColor(selected: UIColor, normal: UIColor) {
    let views = self.subviews
    var positions = [viewPosition]()
    for (i, view) in views.enumerated() {
        let position = viewPosition(originX: view.frame.origin.x, originIndex: i)
        positions.append(position)
    }
    positions.sort(by: { $0.originX < $1.originX })

    for (i, position) in positions.enumerated() {
        let view = self.subviews[position.originIndex]
        if i == self.selectedSegmentIndex {
            view.tintColor = selected
        } else {
            view.tintColor = normal
        }
    }
}
}
override func viewDidLoad() {
    super.viewDidLoad()

    mySegment.removeBackgroundColors()
    mySegment.backgroundColor = .clear
    mySegment.updateTintColor(selected: myNavigationColor, normal: text1Color)
}

0

这个解决方案仅适用于两个段,但可以轻松扩展以用于您需要的任意数量。

首先,我建议创建一个枚举:

    enum SegmentedSections: Int { 
      case first, 
      case second
    }

创建一个函数,在viewDidLoad中调用此函数,并且每当segmentedControl的.valueChanged事件发生时都调用它:
func setProperSegmentedControlColoring(_ segment: UISegmentedControl, type: SegmentedSections) {
    setSeparatorImages(for: segment, with: type)
    let subviews = segment.subviews
    let sortedViews = subviews.sorted(by: { $0.frame.origin.x < $1.frame.origin.x })

    for (index, view) in sortedViews.enumerated() {
        switch type {
        case .first:
            if index == segment.selectedSegmentIndex {
                view.tintColor = .red
            } else {
                view.tintColor = .blue
            }
        case .second:
            if index == segment.selectedSegmentIndex {
                view.tintColor = .blue
            } else {
                view.tintColor = .red
            }
        }
    }
}

另外,您还需要相应更改分隔符图像:

func setSeparatorImages(for segment: UISegmentedControl, with type: EarnType) {
    switch type {
    case .first:
        let image = UIImage(color: .red)
        segment.setDividerImage(image, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
    case .second:
        let image = UIImage(color: .blue)
        segment.setDividerImage(image, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
    }
}

此外,您需要拥有一个UIImage的扩展。您可以在这里找到它。

0

从iOS 13开始,可以使用属性selectedSegmentTintColor来设置分段控件的色调颜色!

所以只需执行以下操作:

segmentControl.selectedSegmentTintColor = .red

如果您的设备运行的是 iOS 13 及以下版本,

   if #available(iOS 13.0, *) {
      segmentControl.selectedSegmentTintColor = .red
    } else {
      // Fallback on earlier versions
      // Solution posted by David can be used here
    }

但是你能使用它在每个按钮上设置不同的色调吗?这就是这个问题的重点。抱歉,我已经没有 xCode 来自测了。 - Automate This

-1
class MyUISegmentedControl: UISegmentedControl {

    required init(coder aDecoder: NSCoder){
        super.init(coder: aDecoder)!
        for subViewOfSegment: UIView in subviews {
            subViewOfSegment.tintColor = UIColor.red
        }
    }
}

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