9

In my Swift app, I dont know how to change BGColor for selected segment alone, in UISegmentControl. I have tried a lot, only tint color is changing. I got success in Objective - C. I dont know how to convert that to SWIFT. Kindly guide me. My codings below:

Objective C

- (IBAction)mySeg:(UISegmentedControl *)sender {

    for (int i=0; i<[sender.subviews count]; i++)
    {
        if ([[sender.subviews objectAtIndex:i]isSelected] )
        {
            UIColor *tintcolor=[UIColor colorWithRed:255.0/255.0 green:0/255.0 blue:0/255.0 alpha:1.0];
            [[sender.subviews objectAtIndex:i] setTintColor:tintcolor];  //HERE SELECTED SEGMENT COLOR ALONE CHANGING
        }
        else
        {
            [[sender.subviews objectAtIndex:i] setTintColor:nil];
        }
    }
}

Swift

@IBAction func mySegAcn(sender: UISegmentedControl) {
for(var i : Int = 0; i < sender.subviews.count; i++)
{
    if((sender.subviews[i].isSelected) != nil) 
    {
        //var tint_Color =  UIColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)

        (sender.subviews[i] as! UIView).tintColor = UIColor .redColor()  //HERE WHOLE TINT COLOR IS CHANGING
    }

    else
    {

        (sender.subviews[i] as! UIView).tintColor = nil
    }
}
}

Output

enter image description here

McDonal_11
  • 3,935
  • 6
  • 24
  • 55
  • it's working fine on my side..there may be some other issue but code for change selected segment is that only.. – Kavita Sep 08 '15 at 07:12
  • Hi @Katty ,, Did u chk both code? In Obj - C, Selected segment alone color change, In Swift, Selected segment as well as tint color of all segments changing. That is my issue. – McDonal_11 Sep 08 '15 at 07:16

9 Answers9

12

I tried using isSelected, it gives me an error saying the method does not exist. Here is another way to do it without the isSelected method

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()
gabo
  • 1,133
  • 8
  • 7
5

Swift 4

let sortedViews = segmentedControl.subviews.sorted( by: { $0.frame.origin.x < $1.frame.origin.x } )

for (index, view) in sortedViews.enumerated() {
    if index == segmentedControl.selectedSegmentIndex {
        view.tintColor = UIColor.red
    } else {
        view.tintColor = UIColor.green
    }
}
Abhishek Jain
  • 4,557
  • 2
  • 32
  • 31
5

iOS 13 and Swift 5

Try using this extension:

extension UISegmentedControl {
    func setSelectedSegmentForegroundColor(_ foregroundColor: UIColor, andTintColor tintColor: UIColor) {
        if #available(iOS 13.0, *) {
            self.setTitleTextAttributes([.foregroundColor: foregroundColor], for: .selected)
            self.selectedSegmentTintColor = tintColor;
        } else {
            self.tintColor = tintColor;
        }
    }
}

Call:

segmentedControlName.setSelectedSegmentForegroundColor(.white, andTintColor: .black);
lchamp
  • 6,592
  • 2
  • 19
  • 27
Victor Rius
  • 4,566
  • 1
  • 20
  • 28
2

The issue with the Swift code is the treatment of the optional: (sender.subviews[i].isSelected) != nil will be true even if isSelected is false.

Try writing your loop using the Swift syntax

for subview in (sender.subviews as! [UIView]) {
   if (subview.isSelected) {
       subview.tintColor = UIColor.redColor()
   } 
   else {
       subview.tintColor = nil
   } 
}

At least in that loop you are actually testing whether or not the subview is selected instead of whether or not the view is not nil.

MiKL
  • 1,850
  • 1
  • 15
  • 23
1

As per @MiKL suggestion, following Code is working fine.

Code

for(var i : Int = 0; i < sender.subviews.count; i++)
        {
            if ((sender.subviews[i].isSelected) == true)
            {
                (sender.subviews[i] as! UIView).tintColor = UIColor .redColor()
            }
            else
            {
                (sender.subviews[i] as! UIView).tintColor = nil
            }
        }
McDonal_11
  • 3,935
  • 6
  • 24
  • 55
1

Updating @gabo answer in Swift 4.1 as I donot have permission to modify his answer.

@IBAction func indexChanged(sender: UISegmentedControl) { 

     let sortedViews = sender.subviews.sorted( by: { $0.frame.origin.x < $1.frame.origin.x } )

            for (index, view) in sortedViews.enumerated() {
                if index == sender.selectedSegmentIndex { //When selected
                    view.tintColor = UIColor.blue
                } else {//Unselected
                    view.tintColor = nil
                }
            }

}

Initially selected segment in viewDidLoad

let sortedViews = segmentedControlOutletVariable.subviews.sorted( by: { $0.frame.origin.x < $1.frame.origin.x } )
sortedViews[0].tintColor = UIColor.blue //Default Selection color
Alphonse R. Dsouza
  • 2,011
  • 1
  • 14
  • 11
1

I wanted to have a different colour with each Segment that was highlighted and found a VERY simple answer to my solution, this is to just simply change the colour of the selectedSegmentTintColor on each Case within my Switch.

This is using Swift 5.1

@IBOutlet weak var mySegOutlet: UISegmentedControl!


@IBAction func mySegmentControl(_ sender: UISegmentedControl) {
    switch sender.selectedSegmentIndex {
    case 0:
        mySegOutlet.selectedSegmentTintColor = .red

    case 1:
        mySegOutlet.selectedSegmentTintColor = .yellow

    case 2:
        mySegOutlet.selectedSegmentTintColor = .white

    case 3:
        mySegOutlet.selectedSegmentTintColor = .orange

    case 4:
        mySegOutlet.selectedSegmentTintColor = .red

    default:
        break
    }  //end of Switch
}

Remember to set the text colour in ViewDidLoad - Colour mixing matters.

mySegOutlet.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.selected)

This Works very smoothly for me and gives me the desired results with rounded corners and nice visual effects, as well you're not setting the background colour of the Segment, and therefore you don't need to reset the background colour when moving away from the segment.

David_2877
  • 257
  • 4
  • 7
0

Your code for change color is working fine the only problem is if condition that is not working in same manner it working in objective c.

change if condition with

 if(sender.selectedSegmentIndex != i)

@IBAction func action(sender: UISegmentedControl) {
    for(var i : Int = 0; i < sender.subviews.count; i++)
    {
        if(sender.selectedSegmentIndex != i)
        {
            //var tint_Color =  UIColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)

            (sender.subviews[i] as! UIView).tintColor = UIColor .redColor()

            //HERE WHOLE TINT COLOR IS CHANGING
        }

        else
        {

            (sender.subviews[i] as! UIView).tintColor = nil
        }
    }
}
Kavita
  • 176
  • 8
0

For Swift: On segment control clicked (@IBAction) use following code

@IBAction func segmentChanged(sender: UISegmentedControl) {
    let sortedSubViews = sender.subviews.sort { $0.frame.minX < $1.frame.minX }
    let selectedSegment = sortedSubViews[sender.selectedSegmentIndex]
    sortedSubViews.forEach({
        // white is selected color and cayan color is for not selected item 
        $0.tintColor = $0 == selectedSegment ? UIColor.whiteColor() : UIColor.cyanColor()
    })
}

create outlet of segmentedControl and on viewDidLoad you need to call above function.

self.segmentChanged(segmentedControl)
Bibek
  • 3,689
  • 3
  • 19
  • 28