如何在UISegmentedControl中仅显示所选项目的底部边框?

时间:2017-03-13 02:09:04

标签: ios swift3 instagram uisegmentedcontrol

我对iOS开发非常陌生,在为课程构建应用程序时遇到了一些麻烦。

我创建了一个分段控件,并在包含分段控件的视图控制器类中调用其init函数(如下所示)。我能够从分段控件类中删除分段控件的所有边框和分隔符,如下所示:

import Foundation
import UIKit

class CashSegmentedControl: UISegmentedControl{

func initUI(){
    removeBorders()
}

func removeBorders(){
    self.tintColor = UIColor.clear

}

This is what my result looks like currently

我希望在选择细分受众群时,每个细分受众群下都有一条线(类似于Instagram)

This is a sample of what I want it to look like

我经常搜索并在StackOverflow上发布一些帖子,但它们似乎适用于旧版本的Swift。我真的很感激这方面的任何帮助,如果有一个更好的解决方案来定制边界(除了我所做的),我想要了解更多!

非常感谢:)

6 个答案:

答案 0 :(得分:27)

在单独的swift文件中添加以下代码(命令+ N - >新文件):

extension UISegmentedControl{
    func removeBorder(){
        let backgroundImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: self.bounds.size)
        self.setBackgroundImage(backgroundImage, for: .normal, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .selected, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .highlighted, barMetrics: .default)

        let deviderImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: CGSize(width: 1.0, height: self.bounds.size.height))
        self.setDividerImage(deviderImage, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
        self.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.gray], for: .normal)
        self.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)], for: .selected)
    }

    func addUnderlineForSelectedSegment(){
        removeBorder()
        let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let underlineHeight: CGFloat = 2.0
        let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
        let underLineYPosition = self.bounds.size.height - 1.0
        let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
        let underline = UIView(frame: underlineFrame)
        underline.backgroundColor = UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)
        underline.tag = 1
        self.addSubview(underline)
    }

    func changeUnderlinePosition(){
        guard let underline = self.viewWithTag(1) else {return}
        let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
        UIView.animate(withDuration: 0.1, animations: {
            underline.frame.origin.x = underlineFinalXPosition
        })
    }
}

extension UIImage{

    class func getColoredRectImageWith(color: CGColor, andSize size: CGSize) -> UIImage{
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        let graphicsContext = UIGraphicsGetCurrentContext()
        graphicsContext?.setFillColor(color)
        let rectangle = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        graphicsContext?.fill(rectangle)
        let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return rectangleImage!
    }
}

然后从您的segmentedControl.addUnderlineForSelectedSegment()方法调用viewDidLoad()后,为分段控件创建@IBAction方法,如下所示:

@IBAction func segmentedControlDidChange(_ sender: UISegmentedControl){
        segmentedControl.changeUnderlinePosition()
    }

然后从此方法中调用segmentedControl.changeUnderlinePosition()

不要忘记将故事板中的分段控件连接到刚刚创建的@IBAction方法。

非常重要:不要忘记在故事板中使用自动布局来确定分段控件的大小和位置。

结果如下:

enter image description here

enter image description here

enter image description here

随意提出您可能遇到的任何其他问题:)

答案 1 :(得分:5)

A.Jam在 Xcode 9.3,Swift 4.2版本中的回答

import UIKit

extension UISegmentedControl {
    func removeBorder(){
        let backgroundImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: self.bounds.size)
        self.setBackgroundImage(backgroundImage, for: .normal, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .selected, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .highlighted, barMetrics: .default)

        let deviderImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: CGSize(width: 1.0, height: self.bounds.size.height))
        self.setDividerImage(deviderImage, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
        self.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.gray], for: .normal)
        self.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)], for: .selected)
    }

    func addUnderlineForSelectedSegment(){
        removeBorder()
        let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let underlineHeight: CGFloat = 2.0
        let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
        let underLineYPosition = self.bounds.size.height - 1.0
        let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
        let underline = UIView(frame: underlineFrame)
        underline.backgroundColor = UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)
        underline.tag = 1
        self.addSubview(underline)
    }

    func changeUnderlinePosition(){
        guard let underline = self.viewWithTag(1) else {return}
        let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
        UIView.animate(withDuration: 0.1, animations: {
            underline.frame.origin.x = underlineFinalXPosition
        })
    }
}

extension UIImage {

    class func getColoredRectImageWith(color: CGColor, andSize size: CGSize) -> UIImage{
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        let graphicsContext = UIGraphicsGetCurrentContext()
        graphicsContext?.setFillColor(color)
        let rectangle = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        graphicsContext?.fill(rectangle)
        let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return rectangleImage!
    }
}

答案 2 :(得分:1)

你可以用这个

let segmentBottomBorder = CALayer()
segmentBottomBorder?.borderColor = UIColor.blue.cgColor
segmentBottomBorder?.borderWidth = 3
let x = CGFloat(sender.selectedSegmentIndex) * width
let y = sender.frame.size.height - (segmentBottomBorder?.borderWidth)!
let width: CGFloat = sender.frame.size.width/3
segmentBottomBorder?.frame = CGRect(x: x, y: y, width: width, height: (segmentBottomBorder?.borderWidth)!)
sender.layer.addSublayer(segmentBottomBorder!)

答案 3 :(得分:0)

A.Jam在Xcode 10.1 ,Swift 4.2 版本-没有UIImage Extension

中的答案

和所有segmentIndexes的灰色下划线

extension UISegmentedControl {

  func removeBorder(){

    self.tintColor = UIColor.clear
    self.backgroundColor = UIColor.clear
    self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.orange], for: .selected)
    self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.gray], for: .normal)

  }

  func setupSegment() {
    self.removeBorder()
    let segmentUnderlineWidth: CGFloat = self.bounds.width
    let segmentUnderlineHeight: CGFloat = 2.0
    let segmentUnderlineXPosition = self.bounds.minX
    let segmentUnderLineYPosition = self.bounds.size.height - 1.0
    let segmentUnderlineFrame = CGRect(x: segmentUnderlineXPosition, y: segmentUnderLineYPosition, width: segmentUnderlineWidth, height: segmentUnderlineHeight)
    let segmentUnderline = UIView(frame: segmentUnderlineFrame)
    segmentUnderline.backgroundColor = UIColor.gray

    self.addSubview(segmentUnderline)
    self.addUnderlineForSelectedSegment()

  }
  func addUnderlineForSelectedSegment(){

    let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
    let underlineHeight: CGFloat = 2.0
    let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
    let underLineYPosition = self.bounds.size.height - 1.0
    let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
    let underline = UIView(frame: underlineFrame)
    underline.backgroundColor = UIColor.orange
    underline.tag = 1
    self.addSubview(underline)
  }

  func changeUnderlinePosition(){
    guard let underline = self.viewWithTag(1) else {return}
    let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
    underline.frame.origin.x = underlineFinalXPosition

  }
}

答案 4 :(得分:0)

要在iOS13中创建WHITE分段控件

segmentedControl.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.black, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17, weight: .medium)], for: .selected)
segmentedControl.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17, weight: .medium)], for: .normal)
    if #available(iOS 13.0, *) {
        let dividerImage = UIImage(color: .white, size: CGSize(width: 1, height: 32))
        segmentedControl.setBackgroundImage(UIImage(named: "w1"), for: .normal, barMetrics: .default)
        segmentedControl.setBackgroundImage(UIImage(named: "w2"), for: .selected, barMetrics: .default)
        segmentedControl.setDividerImage(dividerImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
        segmentedControl.layer.cornerRadius = 0
        segmentedControl.layer.masksToBounds = true
    }

extension UIImage {
    convenience init(color: UIColor, size: CGSize) {
        UIGraphicsBeginImageContextWithOptions(size, false, 1)
        color.set()
        let ctx = UIGraphicsGetCurrentContext()!
        ctx.fill(CGRect(origin: .zero, size: size))
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        self.init(data: image.pngData()!)!
    }
}

w1图片为enter image description here的位置 并且w2图片为enter image description here

A.Jams回答 SWIFT 5.1 Xcode 11.3 以创建灰色控件

import Foundation
extension UISegmentedControl {

    func removeBorder(){

        self.tintColor = UIColor.clear
        self.backgroundColor = UIColor.clear
        self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.stavkrugDarkBlue], for: .selected)
        self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.gray], for: .normal)
        if #available(iOS 13.0, *) {
            self.selectedSegmentTintColor = UIColor.clear
        } 

    }

    func setupSegment() {
        self.removeBorder()
        let segmentUnderlineWidth: CGFloat = self.bounds.width
        let segmentUnderlineHeight: CGFloat = 2.0
        let segmentUnderlineXPosition = self.bounds.minX
        let segmentUnderLineYPosition = self.bounds.size.height - 1.0
        let segmentUnderlineFrame = CGRect(x: segmentUnderlineXPosition, y: segmentUnderLineYPosition, width: segmentUnderlineWidth, height: segmentUnderlineHeight)
        let segmentUnderline = UIView(frame: segmentUnderlineFrame)
        segmentUnderline.backgroundColor = UIColor.clear

        self.addSubview(segmentUnderline)
        self.addUnderlineForSelectedSegment()
    }

    func addUnderlineForSelectedSegment(){

        let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let underlineHeight: CGFloat = 2.0
        let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
        let underLineYPosition = self.bounds.size.height - 1.0
        let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
        let underline = UIView(frame: underlineFrame)
        underline.backgroundColor = UIColor.stavkrugDarkBlue
        underline.tag = 1
        self.addSubview(underline)


    }

    func changeUnderlinePosition(){
        guard let underline = self.viewWithTag(1) else {return}
        let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
        underline.frame.origin.x = underlineFinalXPosition

    }
}

答案 5 :(得分:0)

我自己使用公认的答案和其他人的一些花絮实现了这一点。

变化:

  1. 它将支持深色/浅色模式的外观变化,segmentedControl TextColor 和 BGColor 也会相应变化。这也将摆脱分段控制分隔线。
  2. 我将 setupSegment()addUnderlineForSelectedSegment() 包裹在 DispatchQueue.main.asyn() 中,以便手机方向的更改获得正确的宽度。
  3. 添加了一个名为 removeUnderline() 的新函数,以在绘制新下划线之前去除之前的下划线。这是为了使下划线宽度与该方向内的实际宽度相对应。

放东西的地方。

viewDidLoad() {
  yourUISegmentedControl.setupSegment()
}

  /// https://stackoverflow.com/a/57943610/14414215
  /// Use this to detect changes in User Interface (Dark or Light Mode) then setup the UISegmentedControl Appearance
  override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    yourUISegmentedControl.setupSegment()
  }

注意:我确实尝试放入 viewWillTransition() 但这只会检测手机方向的变化,而不是暗/亮模式外观的变化。将其放入 traitCollectionDidChange() 实际上会同时满足暗/亮模式和方向变化。

下面的其余部分您可以放入它自己的 Swift 文件中。

import UIKit

extension UISegmentedControl {
  
  func removeBorder(){
    var bgcolor: CGColor
    var textColorNormal: UIColor
    var textColorSelected: UIColor
    
    if self.traitCollection.userInterfaceStyle == .dark {
      bgcolor = UIColor.black.cgColor
      textColorNormal = UIColor.gray
      textColorSelected = UIColor.white
    } else {
      bgcolor = UIColor.white.cgColor
      textColorNormal = UIColor.gray
      textColorSelected = UIColor.black
    }
    
    let backgroundImage = UIImage.getColoredRectImageWith(color: bgcolor, andSize: self.bounds.size)
    self.setBackgroundImage(backgroundImage, for: .normal, barMetrics: .default)
    self.setBackgroundImage(backgroundImage, for: .selected, barMetrics: .default)
    self.setBackgroundImage(backgroundImage, for: .highlighted, barMetrics: .default)
    
    let deviderImage = UIImage.getColoredRectImageWith(color: bgcolor, andSize: CGSize(width: 1.0, height: self.bounds.size.height))
    self.setDividerImage(deviderImage, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
    self.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: textColorNormal], for: .normal)
    self.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: textColorSelected], for: .selected)
    
  }
  
  func setupSegment() {
    DispatchQueue.main.async() {

    self.removeBorder()
// I Commented all these out as I didn't see what exactly it's used for as the color is clear anyways
//    let segmentUnderlineWidth: CGFloat = self.bounds.width
//    print("setupSegment UnderlineWidth:\(segmentUnderlineWidth) width:\(UIScreen.main.bounds.width) \(self.bounds.size.width)")
//    let segmentUnderlineHeight: CGFloat = 10.0
//    let segmentUnderlineXPosition = self.bounds.minX
//    let segmentUnderLineYPosition = self.bounds.size.height - 4.0
//    let segmentUnderlineFrame = CGRect(x: segmentUnderlineXPosition, y: segmentUnderLineYPosition, width: segmentUnderlineWidth, height: segmentUnderlineHeight)
//    let segmentUnderline = UIView(frame: segmentUnderlineFrame)
//    segmentUnderline.backgroundColor = UIColor.red
//
//    self.addSubview(segmentUnderline)
    self.addUnderlineForSelectedSegment()
    }
  }
  
  func addUnderlineForSelectedSegment(){
    DispatchQueue.main.async() {
      self.removeUnderline()
      let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
      let underlineHeight: CGFloat = 10.0
      let underlineXPosition = CGFloat(self.selectedSegmentIndex * Int(underlineWidth))
      let underLineYPosition = self.bounds.size.height - 4.0
      let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
      let underline = UIView(frame: underlineFrame)
      underline.backgroundColor = UIColor.darkGray
      underline.tag = 1
      self.addSubview(underline)
      
    }
  }
  
  func changeUnderlinePosition(){
    guard let underline = self.viewWithTag(1) else {return}
    let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
    underline.frame.origin.x = underlineFinalXPosition
  }
  
  func removeUnderline(){
    guard let underline = self.viewWithTag(1) else {return}
    underline.removeFromSuperview()
  }
}


extension UIImage{
  
  class func getColoredRectImageWith(color: CGColor, andSize size: CGSize) -> UIImage{
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
    let graphicsContext = UIGraphicsGetCurrentContext()
    graphicsContext?.setFillColor(color)
    let rectangle = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
    graphicsContext?.fill(rectangle)
    let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return rectangleImage!
  }
}