3

I have three buttons named One, Two, and Three, and a function buttonPressed for collection of those three buttons as follows.

var btnTag = [Int]()

@IBAction func buttonPressed(_ sender: UIButton) {

    guard let button = sender as UIButton? else { return }
    if(btnTag.contains((sender as AnyObject).tag!))
    {
        if let index = btnTag.index(of: (sender as AnyObject).tag!)
        {
            btnTag.remove(at: index)
        }
    }
    else
    {
       btnTag.append((sender as AnyObject).tag!)
    }

    if !button.isSelected {
        button.isSelected = true
        button.setTitleColor(.red, for: .normal)
    }
    else
    {
        button.isSelected = false
        button.setTitleColor(.white, for: .normal)
    }
}

I like to change color of button as red when clicked and then when I click other button the previous button color as white. So if I press One, I want One to be red and then when I pres Two, I want Two to be red and One as white. I did the above but it is allowing multiple selection and not able to deselect previous changes. How do I solve this?

Coder221
  • 1,403
  • 2
  • 19
  • 35
  • you need to unselect all buttons before select any other – Reinier Melian Aug 17 '18 at 10:44
  • So, in that case, I have to create separate outlets for each button. Is there a way, I can do it without creating separate outlets fro each button? – Coder221 Aug 17 '18 at 10:55
  • you can see my answer or there is something called `IBOutletCollection ` that will make you code look much cleaner, by having only 1 outlet for all buttons – kathayatnk Aug 17 '18 at 10:59

2 Answers2

4

You can simply create collection of UIButton. After that assign tag of all three UIButton from Storyboard.

@IBOutlet var buttons: [UIButton]!

Use above collection and connect all the button with Outlet. also connect below action with all buttons.

@IBAction func buttonPressed(_ sender: UIButton) {
    buttons.forEach { $0.isSelected = false
        $0.setTitleColor(.white, for: .normal)
    }

    buttons[sender.tag].setTitleColor(.red, for: .normal)
    buttons[sender.tag].isSelected = true
}
AaoIi
  • 8,288
  • 6
  • 45
  • 87
Jaydeep Vora
  • 6,085
  • 1
  • 22
  • 40
  • This is what I was looking for. Nice explanation :) – Coder221 Aug 17 '18 at 11:03
  • Does `buttons[sender.tag]` work? I was under the impression that outlet collections were in no specific order. – vacawama Aug 17 '18 at 11:16
  • [This question and top answer](https://stackoverflow.com/q/15189922/1630618) discuss the issue. Apparently outlet collections do have an order, but it is a bad practice to depend on that order because there is no way to verify it. – vacawama Aug 17 '18 at 11:22
  • Finally, wouldn't `sender.setTitleColor(.red, for: .normal)` and `sender.isSelected = false` be sufficient. It doesn't matter where the `sender` is in the outlet collection. – vacawama Aug 17 '18 at 11:29
  • @vacawama, if we give tag numbers to buttons, doesn't it work? – Coder221 Aug 18 '18 at 13:16
  • My problem is with the statement `buttons[sender.tag].isSelected = true`. It is a bad practice to depend on the order of items in an `@IBOutlet` collection. I don't see why that is necessary anyway. `sender.isSelected = true` should be sufficient here. – vacawama Aug 18 '18 at 13:30
  • @vacawama, got it, you are correct. Thanks for poinitng that out. – Coder221 Aug 18 '18 at 13:51
1

Since you are highlighting only one button at the moment, you don't need tag array instead the reference to all buttons.

//Let say these are my button refrence
@IBOutlet weak var button1: UIButton!
@IBOutlet weak var button2: UIButton!
@IBOutlet weak var button3: UIButton!

All of those buttons are targeting same method

@IBAction func buttonPressed(_ sender: UIButton) {

     //clear all button selected state
     clearSelectedState()

     //select the button that was clicked
     sender.isSelected = true
     sender.setTitleColor(.red, for: .normal)
}


func clearSelectedState() {

     button1.isSelected = false
     button1.setTitleColor(.white, for: .normal)

     .... proceed to do for others
}

Now in clearSelectedState method I don't like the repetition of code. So what we can do is put the reference in array and do something like

///this can be replaced in clear state method
[button1, button2, button3,...].forEach {
       $0.isSelected = false
       $0.setTitleColor(.white, for: .normal)
}
kathayatnk
  • 975
  • 1
  • 7
  • 9