I'm trying to customize my segement control like below image. So, far I was able to customize its text attributes and color. Only problem is with the border. As per the below image, if my first segment is selected the border should apply to first segment top, right and second segment's bottom. And if my second segment is selected it should be the reverse ie, second segment top, left and first segments bottom.
Things done so far
UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.blue], for: .selected)
UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.green], for: .normal)
You can do this by adding an extension to UISegmentedControl
. Try this.
extension UISegmentedControl {
private func defaultConfiguration(font: UIFont = UIFont.boldSystemFont(ofSize: 12), color: UIColor = UIColor.gray) {
let defaultAttributes = [
NSAttributedStringKey.font.rawValue: font,
NSAttributedStringKey.foregroundColor.rawValue: color
]
setTitleTextAttributes(defaultAttributes, for: .normal)
}
private func selectedConfiguration(font: UIFont = UIFont.boldSystemFont(ofSize: 12), color: UIColor = UIColor.blue) {
let selectedAttributes = [
NSAttributedStringKey.font.rawValue: font,
NSAttributedStringKey.foregroundColor.rawValue: color
]
setTitleTextAttributes(selectedAttributes, for: .selected)
}
private func removeBorder(){
let backgroundImage = getColoredRectImageWith(color: UIColor.white.cgColor, andSize: CGSize(width: self.bounds.size.width, height: self.bounds.size.height), yOffset: 2)
let backgroundImage2 = getColoredRectImageWith(color: UIColor.lightGray.cgColor, andSize: CGSize(width: self.bounds.size.width, height: self.bounds.size.height))
self.setBackgroundImage(backgroundImage2, for: .normal, barMetrics: .default)
self.setBackgroundImage(backgroundImage, for: .selected, barMetrics: .default)
self.setBackgroundImage(backgroundImage, for: .highlighted, barMetrics: .default)
let deviderImage = getColoredRectImageWith(color: UIColor.gray.cgColor, andSize: CGSize(width: 1.0, height: self.bounds.size.height))
self.setDividerImage(deviderImage, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
defaultConfiguration( color: UIColor.green)
selectedConfiguration(color: UIColor.blue)
}
func addUnderlineForSelectedSegment(){
removeBorder()
let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
let underlineHeight: CGFloat = 1.0
let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
let underLineYPosition = self.bounds.size.height - 2.0
let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
let topUnderline = UIView(frame: underlineFrame)
topUnderline.backgroundColor = UIColor.gray
topUnderline.tag = 1
topUnderline.frame.origin.y = self.frame.origin.y
self.addSubview(topUnderline)
let bottomUnderline = UIView(frame: underlineFrame)
bottomUnderline.backgroundColor = UIColor.gray
bottomUnderline.tag = 2
bottomUnderline.frame.origin.x = topUnderline.frame.maxX
self.addSubview(bottomUnderline)
}
func changeUnderlinePosition(){
guard let topUnderline = self.viewWithTag(1) else {return}
let topUnderlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
topUnderline.frame.origin.x = topUnderlineFinalXPosition
guard let bottomUnderline = self.viewWithTag(2) else {return}
let underlineFinalXPosition = (selectedSegmentIndex == 0) ? topUnderline.frame.maxX : self.frame.origin.x
bottomUnderline.frame.origin.x = underlineFinalXPosition
}
private func getColoredRectImageWith(color: CGColor, andSize size: CGSize,yOffset:CGFloat = 0, hOffset:CGFloat = 0) -> UIImage{
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let graphicsContext = UIGraphicsGetCurrentContext()
graphicsContext?.setFillColor(color)
let rectangle = CGRect(x: 0.0, y: 0.0 + yOffset, width: size.width, height: size.height - hOffset)
graphicsContext?.fill(rectangle)
let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return rectangleImage!
}
}
Usage
In viewDidLoad
add
mySegmentControl.addUnderlineForSelectedSegment()
And in your segment control action use
@IBAction func mySegmentControl(_ sender: UISegmentedControl) {
mySegmentControl.changeUnderlinePosition()
}