Search code examples
arraysswiftbuttonuisegmentedcontrol

Save button states when we move segmented control


We have for example segmented control, and array of buttons. If we select one of button in one segment - button need to save state, and than we move to another segment and choose another button. but if we back to previous button - it should be in the position we chose earlier. that is, each of the segments must store the state of the buttons. how to do it better?

struct SegmentsModel {
      let title: String
      var answered: Bool
      var buttonIndex: Int? = nil
}

@objc private func buttonsTapped(_ sender: UIButton) {
        let _ = buttons.map({$0.isSelected = false})
         sender.isSelected = true
        guard let index = buttons.firstIndex(of: sender) else { return }

        switch index {
        case 0:
            selectedSegment(segmentedControl, buttonSelected: true, indexButton: 0)
        case 1:
            selectedSegment(segmentedControl, buttonSelected: true, indexButton: 1)
        case 2:
            selectedSegment(segmentedControl, buttonSelected: true, indexButton: 2)
        case 3:
            selectedSegment(segmentedControl, buttonSelected: true, indexButton: 3)
        default:
            break
        }
}

     @objc private func selectedSegment(_ sender: UISegmentedControl, 
                                        buttonSelected: Bool, indexButton: Int) {
        let currentIndex = sender.selectedSegmentIndex
        
        if buttonSelected == true {
            buttons[indexButton].isSelected = true
        } else {
            let _ = buttons.map({$0.isSelected = false})
        }
        
        switch currentIndex {
        case 0:
            arrayOfSegments[0].answered = buttonSelected
            arrayOfSegments[0].buttonIndex = indexButton
        case 1:
            arrayOfSegments[1].answered = buttonSelected
            arrayOfSegments[1].buttonIndex = indexButton
        case 2:
            arrayOfSegments[2].answered = buttonSelected
            arrayOfSegments[2].buttonIndex = indexButton
        default:
            break
        }
    }
    

Image of the expected UI using UISegmentControl and UIButton


Solution

  • As I mentioned in my comments, there are many ways to achieve what you want. I will share one idea with you.

    I see that you tried to store all your buttons in an array and had to keep looping over them to figure out which button was selected previously. Since you want the code to have simpler logic, I will give you a different idea.

    One way to achieve what you want is using tags. Every UIView object has a tag and the default is set to 0.

    Using storyboard I set the tag of the buttons to 100, 101, 102, 103 and this can be done programmatically also.

    Adding a tag to a UIButton in StoryBoard

    You can choose any integers that you like but it is important to give them some unique numbers so when you try to get a view by tag, you get only the view you want.

    So after setting the tags, this is what I updated in the code.

    struct SegmentsModel {
        let title: String
        var answered = false
        var buttonIndex: Int? = nil
        {
            // Auto-update the value of answered when value of buttonIndex changes
            didSet
            {
                answered = buttonIndex != nil
            }
        }
    }
    

    I did not do any major updates here. I only added a property observer so when the button index gets set, the answered variable also is auto set so you don't have to anything more for this

    Next, below is all the logic to manage the segments and the buttons. I used UIViewController with a StoryBoard so you might need to ignore somethings. All the important code has comments so you can follow along.

    class ViewController: UIViewController {
        
        // I will store the results in this array
        var storedResults: [SegmentsModel] = []
        
        // Segment control outlet
        @IBOutlet weak var segmentControl: UISegmentedControl!
        
        // Array of buttons
        @IBOutlet var buttons: [UIButton]!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // Loop over all segments
            for index in 0 ..< segmentControl.numberOfSegments
            {
                // Initialize and store default results values in storedResults array
                if let segmentTile = segmentControl.titleForSegment(at: index)
                {
                    storedResults.append(SegmentsModel(title: segmentTile))
                }
            }
        }
        
        // Segment action
        @IBAction func selectedSegment(_ sender: UISegmentedControl)
        {
            // Update the UI of buttons
            reloadButtons()
        }
        
        
        @IBAction func buttonTapped(_ sender: UIButton)
        {
            // Reset your buttons
            let _ = buttons.map({$0.isSelected = false})
            
            // Set the current button to selected
            sender.isSelected = true
            
            // Get the current result so we can update it
            var currentResult = storedResults[segmentControl.selectedSegmentIndex]
            
            // Update the current segments selected button index using tag
            currentResult.buttonIndex = sender.tag
            
            // Put the result back into the array as structs as value types
            storedResults[segmentControl.selectedSegmentIndex] = currentResult
        }
        
        // Reload button data
        private func reloadButtons()
        {
            // Reset your buttons
            let _ = buttons.map({$0.isSelected = false})
            
            let currentResult = storedResults[segmentControl.selectedSegmentIndex]
            
            // Check if current index has a selected button and if it does retrieve it
            // with a tag
            if let selectedButtonIndex = currentResult.buttonIndex,
               let selectedButton = view.viewWithTag(selectedButtonIndex) as? UIButton
            {
                // Show the selected button in the UI
                selectedButton.isSelected = true
            }
        }
    }
    

    The end result achieved can be seen in this youtube video which I believe is what you wanted.