Reputation: 31364
Starting to learn Swift and am attempting to convert this ObjectiveC code:
[[mySegmentedControl.subviews objectAtIndex:0] setTintColor:[UIColor blueColor]]
This correctly sets the tint color of the first segment.
This is the closest I've come to getting a Swift version of the same code:
mySegmentedControl?.subviews[0].tintColor = UIColor.blueColor()
The error I get is '@Ivalue $T9' is not identical to 'UIColor!!'
I don't understand what this error means. When I look at the .tintColor
method it list UIColor!?
and I haven't found what the !?
together means in Swift yet.
Upvotes: 8
Views: 31254
Reputation: 1156
From iOS 13 onwards, the property selectedSegmentTintColor
can be used to set tint color of segment control!
So simply do:
segmentControl.selectedSegmentTintColor = .red
If you are supporting below iOS 13,
if #available(iOS 13.0, *) {
segmentControl.selectedSegmentTintColor = .red
} else {
// Fallback on earlier versions
// Solution posted by David can be used here
}
Upvotes: 0
Reputation: 327
For Swift 5.1 i found to work:
//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
I place these within the Switch Statement used to capture the Action of the button when pressed.
Upvotes: 2
Reputation: 2777
This code works fine with latest version of Swift as on August 2019 (Swift 3.0)
Here this code I have implemented is the extension for the Segment control and can be used for all the segment controls in the application, where the set of code has to defined in application class.
Extension method can be used directly in the application, also you can add all the settings to same method or different methods in extension class, as shown below.
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!
}
}
Everywhere for the segments the below code can be used
self.mySegment.setSegmentStyle()
Upvotes: 5
Reputation: 126
The solution is only for two segments but it could be easily extended to use for as much as you need. Firstly I will suggest to create an enum:
enum SegmentedSections: Int {
case first,
case second
}
Than create a funciton, and call this function in viewDidLoad, and also each time call it when .valueChanged happens in segmentedControl:
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
}
}
}
}
Also you will need to change divider image accordingly:
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)
}
}
Also, you will need to have an extension for UIImage. You can find it here.
Upvotes: 0
Reputation: 63
sender.subviews.sort
doesn't work in Swift 4 and remove border is referenced on 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)
}
Upvotes: 0
Reputation: 4437
After analyzing and trying lots of answers to this and other similar questions I realized that with a 3rd-party custom segmented control it will be much easier and safer to do customizations than trying to hack Apple's UISegmentedControl.
Here's an example of customization with XMSegmentedControl (Swift 3).
Some in code:
mySegmentControl.delegate = self
mySegmentControl.font = UIFont.systemFont(ofSize: 12)
And some in the Interface Builder (can be done in code too if you want):
The result is:
In my case it looks very much like system one, but still there are minor differences I had to do exactly as designer wanted it to be.
Note that XMSegmentedControl doesn't allow to have different background colors for different segments, but you can easily add this if needed, since it is a simple .swift file, which is very easy to understand and modify.
Upvotes: 1
Reputation: 489
class MyUISegmentedControl: UISegmentedControl {
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)!
for subViewOfSegment: UIView in subviews {
subViewOfSegment.tintColor = UIColor.red
}
}
}
Upvotes: -1
Reputation: 131
The easiest way I have found is:
segmentControl.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.redColor()], forState: UIControlState.Selected)
Upvotes: 13
Reputation: 1261
For changing the tintColor of the selected segment
Use the value changed event for the UISegmentControl to sort the segments in order by their origin x value, then loop and compare the selectedSegmentIndex property. Here is an example with assuming a segmented control of 4 segments:
@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()
}
}
}
Then in viewDidLoad set the tintColor for the initially selected segment, in this case it is the first:
let sortedViews = segmentedControlOutletVariable.subviews.sort( { $0.frame.origin.x < $1.frame.origin.x } )
sortedViews[0].tintColor = UIColor.blueColor()
Upvotes: 2
Reputation: 8108
This will solve your problem:
var subViewOfSegment: UIView = mySegmentedControl.subviews[0] as UIView
subViewOfSegment.tintColor = UIColor.blueColor()
You can also
(mySegmentedControl.subviews[0] as UIView).tintColor = UIColor .blueColor()
Upvotes: 23