如何在 UISegmentedControl 中只显示所选项目的底部边框?
How to display only bottom border for selected item in UISegmentedControl?
我对 iOS 开发非常陌生,运行 在为课程构建应用程序时遇到了一些麻烦。
我创建了一个分段控件,它的初始化函数(如下所示)在包含分段控件的视图控制器 class 中被调用。我能够从分段控件 class 中删除分段控件的所有边框和分隔线,如下所示:
import Foundation
import UIKit
class CashSegmentedControl: UISegmentedControl{
func initUI(){
removeBorders()
}
func removeBorders(){
self.tintColor = UIColor.clear
}
我希望它在选择段时在每个段下都有一行(类似于 instagram)
我搜索了很多并在 Whosebug 上找到了一些帖子,但它们似乎是针对 Swift 的旧版本。我真的很感激在这件事上的任何帮助,如果有更好的自定义边框的解决方案(除了我所做的),我很乐意了解更多!
非常感谢 :)
你可以使用这个
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!)
在单独的 swift 文件中添加以下代码(command+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!
}
}
然后从您的 viewDidLoad()
方法调用 segmentedControl.addUnderlineForSelectedSegment()
后,为分段控件创建一个 @IBAction
方法,如下所示:
@IBAction func segmentedControlDidChange(_ sender: UISegmentedControl){
segmentedControl.changeUnderlinePosition()
}
然后从此方法中调用 segmentedControl.changeUnderlinePosition()
。
不要忘记将故事板中的分段控件连接到您刚刚创建的 @IBAction
方法。
非常重要:不要忘记在故事板中使用自动布局来确定分段控件的大小和位置。
这是结果:
如有任何其他问题,请随时提出:)
A.Jam在Xcode9.3、Swift4.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!
}
}
A.Jam在Xcode10.1,Swift4.2版本中的回答-没有 UIImage 扩展
以及所有段索引的灰色下划线
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
}
}
在 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 图片是
w2 图像是
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
}
}
我自己使用已接受的答案和其他人的一些花絮实现的。
变化:
- 它将遵循深色/浅色模式外观更改,并且 segmentedControl TextColor 和 BGColor 将相应更改。这也将摆脱分段控制分割线。
- 我将
setupSegment()
和 addUnderlineForSelectedSegment()
包裹在 DispatchQueue.main.asyn()
中,这样 phone 方向的更改将获得正确的宽度。
- 添加了一个名为
removeUnderline()
的新函数,以在绘制新下划线之前去除之前的下划线。这样下划线宽度将对应于该方向内的实际宽度。
放东西的地方。
viewDidLoad() {
yourUISegmentedControl.setupSegment()
}
///
/// 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()
但这只会检测到 phone 方向变化而不是 Dark/Light 模式外观变化。将其放入 traitCollectionDidChange()
实际上会同时满足 Dark/Light 模式和方向变化。
下面的其他内容你可以直接放入它自己的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!
}
}
我对 iOS 开发非常陌生,运行 在为课程构建应用程序时遇到了一些麻烦。
我创建了一个分段控件,它的初始化函数(如下所示)在包含分段控件的视图控制器 class 中被调用。我能够从分段控件 class 中删除分段控件的所有边框和分隔线,如下所示:
import Foundation
import UIKit
class CashSegmentedControl: UISegmentedControl{
func initUI(){
removeBorders()
}
func removeBorders(){
self.tintColor = UIColor.clear
}
我希望它在选择段时在每个段下都有一行(类似于 instagram)
我搜索了很多并在 Whosebug 上找到了一些帖子,但它们似乎是针对 Swift 的旧版本。我真的很感激在这件事上的任何帮助,如果有更好的自定义边框的解决方案(除了我所做的),我很乐意了解更多!
非常感谢 :)
你可以使用这个
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!)
在单独的 swift 文件中添加以下代码(command+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!
}
}
然后从您的 viewDidLoad()
方法调用 segmentedControl.addUnderlineForSelectedSegment()
后,为分段控件创建一个 @IBAction
方法,如下所示:
@IBAction func segmentedControlDidChange(_ sender: UISegmentedControl){
segmentedControl.changeUnderlinePosition()
}
然后从此方法中调用 segmentedControl.changeUnderlinePosition()
。
不要忘记将故事板中的分段控件连接到您刚刚创建的 @IBAction
方法。
非常重要:不要忘记在故事板中使用自动布局来确定分段控件的大小和位置。
这是结果:
如有任何其他问题,请随时提出:)
A.Jam在Xcode9.3、Swift4.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!
}
}
A.Jam在Xcode10.1,Swift4.2版本中的回答-没有 UIImage 扩展
以及所有段索引的灰色下划线
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
}
}
在 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 图片是
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
}
}
我自己使用已接受的答案和其他人的一些花絮实现的。
变化:
- 它将遵循深色/浅色模式外观更改,并且 segmentedControl TextColor 和 BGColor 将相应更改。这也将摆脱分段控制分割线。
- 我将
setupSegment()
和addUnderlineForSelectedSegment()
包裹在DispatchQueue.main.asyn()
中,这样 phone 方向的更改将获得正确的宽度。 - 添加了一个名为
removeUnderline()
的新函数,以在绘制新下划线之前去除之前的下划线。这样下划线宽度将对应于该方向内的实际宽度。
放东西的地方。
viewDidLoad() {
yourUISegmentedControl.setupSegment()
}
///
/// 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()
但这只会检测到 phone 方向变化而不是 Dark/Light 模式外观变化。将其放入 traitCollectionDidChange()
实际上会同时满足 Dark/Light 模式和方向变化。
下面的其他内容你可以直接放入它自己的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!
}
}