【问题标题】:How to set UISegmentedControl Tint Color for individual segment如何为单个段设置 UISegmentedControl Tint Color
【发布时间】:2015-06-01 11:24:48
【问题描述】:

开始学习 Swift 并尝试转换此 ObjectiveC 代码:

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

这会正确设置第一段的色调。


这是我获得相同代码的最接近的 Swift 版本:

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

我得到的错误是'@Ivalue $T9' is not identical to 'UIColor!!'


我不明白这个错误是什么意思。当我查看.tintColor 方法时,它列出了UIColor!?,但我还没有找到!? 在Swift 中的含义。

【问题讨论】:

  • 如果 mySegmentedControl 是 IBOutlet?如果是,请声明它@IBOutlet var mySegmentedControl = UISegmentedControl!那你就不需要了?操作员。这不会解决整个问题,但这是一回事。如果 subviews[0] 可以返回 nil,那么它是一个可选的,所以也许放一个 ! [0] 之后。

标签: swift uisegmentedcontrol


【解决方案1】:

从 iOS 13 开始,selectedSegmentTintColor 属性可用于设置段控件的 tint 颜色!

所以简单地做:

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 来测试自己了。
【解决方案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 语句中,用于捕获按钮按下时的动作。

【讨论】:

    【解决方案3】:

    此代码适用于 2019 年 8 月最新版本的 Swift (Swift 3.0)

    这里我实现的这段代码是Segment控件的扩展,可用于应用程序中的所有Segment控件,其中代码集必须在应用程序类中定义。

    扩展方法可以直接在应用程序中使用,也可以将所有设置添加到扩展类的相同方法或不同方法中,如下所示。

    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()
    

    【讨论】:

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

    该解决方案仅适用于两个细分市场,但可以轻松扩展以根据需要使用。 首先我会建议创建一个枚举:

        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 扩展。 You can find it here.

    【讨论】:

      【解决方案5】:

      sender.subviews.sort 在 Swift 4 中不起作用,How to remove border from segmented control 上引用了删除边框

      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)
      }
      

      【讨论】:

        【解决方案6】:

        在分析并尝试了对这个问题和其他类似问题的大量答案后,我意识到使用 3rd 方自定义分段控件进行自定义比尝试破解 Apple 的 UISegmentedControl 更容易、更安全。

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

        代码中的一些:

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

        还有一些在 Interface Builder 中(如果您愿意,也可以在代码中完成):

        结果是:

        在我的情况下,它看起来非常像系统一,但仍然有一些细微的差别,我必须完全按照设计师的意愿去做。

        请注意,XMSegmentedControl 不允许为不同的段设置不同的背景颜色,但如果需要,您可以轻松添加,因为它是一个简单的 .swift 文件,非常易于理解和修改。

        【讨论】:

        • 我已经更新了我的答案以显示自定义的可能性。自定义分段控件具有简单明了的代码 - 您可以轻松对其进行修改,以便能够为每个分段设置单独的颜色。
        【解决方案7】:
        class MyUISegmentedControl: UISegmentedControl {
        
            required init(coder aDecoder: NSCoder){
                super.init(coder: aDecoder)!
                for subViewOfSegment: UIView in subviews {
                    subViewOfSegment.tintColor = UIColor.red
                }
            }
        }
        

        【讨论】:

          【解决方案8】:

          我找到的最简单的方法是:

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

          【讨论】:

          • 如果您想根据选择更改按钮颜色,这适用于简单的情况。无论自定义 UI 的选定状态如何,我都需要更改特定的索引颜色。
          • Swift 5 版本:segmentControl.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: UIControl.State.selected)
          【解决方案9】:

          用于更改所选片段的 tintColor

          使用 UISegmentControl 的值更改事件按原点 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()
          

          【讨论】:

          • 但是排序后,索引和段索引不一样。
          【解决方案10】:

          这将解决您的问题:

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

          你也可以

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

          【讨论】:

          • 这有记录吗?
          • 我正在更改每个片段的色调颜色,但它反映了错误的颜色,请帮助!!!
          • 有没有办法为突出显示的文本颜色设置不同的颜色?每个段都有不同的颜色
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-04-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-13
          • 2014-05-30
          相关资源
          最近更新 更多