Replace Segmented Controls with Button Arrays

You’ve probably used the Segmented control before like this one

It’s great for some simple uses but lacks flexibility. Besides using only text or single color icon, it doesn’t work in vertical or other arrangements. Let’s look at another solution: Using Button arrays. 

In the exercise files, I’ve set up for you a vertical stack view of buttons, though I could have placed these anywhere with Autolayout.

Open up the assistant editor. I’ve hooked up everything for you but the buttons.  Control-drag from the pizza button to the assistant editor.

While you could use the stack view’s arrangedSubviews array, a more flexible option is a collection of outlets with the outlet collection selection. Instaed of picking an outlet for the connection, select Outlet Collection

I’ll name the collection dessertSelection.

I’ll manually control drag the other buttons to this collection to keep the order correct.  

Control-drag again from the story board.  Make a action didSelectDessert with a sender of UIButton. Drag from the connector to the button so all buttons use the same action. 

Close up the assistant editor and head to ViewController. Each button now will have two states: selected and deselected. We’ll need to deselect everything, and then select the one the user tapped. 

@IBAction func didSelectDessert(_ sender: UIButton) {
     allDeselected()
     selectDessert(button: sender)
}

The power of that array shows up when deselecting the button. You can iterate through the buttons to shut them all off, which in this case is adding a drop shadow I made for you in the shadowOn method: 

func allDeselected(){
        for button in dessertSelection{
           shadowOn(button: button)
        }
    }

Selecting a dessert is doing the opposite to the selected button:

func selectDessert(button:UIButton){
        shadowOff(button: button)    
 }

This gives the selected button a pressed-down look. 

To finish the display, add an allDeselected to viewDidLoad to start fresh. 

override func viewDidLoad() {
        super.viewDidLoad()
        allDeselected()
    }

Run this, which I’ll do on a iPhone XR and you’ll  get buttons that select. For example select mochi

then select Napolean. Mochi pops back up and Napolean depresses.

Of course, you want it to select something.  To do that, make a selectedIndex function.  It will look for a selected condition in the array, which I’ll use the shadow radius, then return the index. 

func selectedIndex()->Int?{
        return dessertSelection.index(where: {(button)-> Bool in
               button.layer.shadowRadius == 1.0})
    }

Back in didSelectDessert, I’ll change the label’s text.  I’ll start by unwrapping the button’s title and the selected index. 

if let selection = selectedIndex() , let text = sender.titleLabel?.text {

}

I’ll set the text, adding 1 since users don’t think to start at zero.  

selectionLabel.text = "#\(selection + 1) " + text

 And finish with an else in case our index or title are missing. 

} else {
            selectionLabel.text = "No Selection"
        }

Run that, and try the buttons.

They now give the selection and the title of the button. This was a simple version of what you can do with an array of buttons. Unlike a segmented control you can arrange and format  them like buttons, which leads to a lot of flexibility in choice controls. 
 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.