问题
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.
回答1:
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()
回答2:
The easiest way I have found is:
segmentControl.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.redColor()], forState: UIControlState.Selected)
回答3:
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()
回答4:
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()
回答5:
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.
回答6:
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)
}
回答7:
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.
回答8:
class MyUISegmentedControl: UISegmentedControl {
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)!
for subViewOfSegment: UIView in subviews {
subViewOfSegment.tintColor = UIColor.red
}
}
}
来源:https://stackoverflow.com/questions/29312447/how-to-set-uisegmentedcontrol-tint-color-for-individual-segment