0

I'm wondering if there is a more efficient way to code an action that is the same with the exception of which button has been pressed and which item in a struct it relates to. Basically, I have a struct of 10 variables all of which are a boolean type and I have 10 buttons. When the user presses the button, I want to check whether it has already been pressed (using the struct) and then change the background of the button depending on the state and reverse the state. I've copied my current code for one of the buttons but thought I should be able to avoid doing this 10 times!

@IBAction func architectureButtonPressed(_ sender: Any) {

        if myInterests.architecture {

             myInterests.architecture = false
             architectureButton.setBackgroundImage(imageUncheckedNarrow, for: .normal)

        } else {

            myInterests.architecture = true
            architectureButton.setBackgroundImage(imageCheckedNarrow, for: .normal)
        }


    }
3
  • Can I see full structure for myInterests? Commented Oct 24, 2018 at 17:48
  • var myInterests = Interests(architecture: false, art: false, fashion: false, history: false, localCulture: false, music: false, nature: false, shopping: false, sport: false, anything: false) Commented Oct 24, 2018 at 17:50
  • Can I see this too Interests Please update the question with it? Commented Oct 24, 2018 at 17:53

3 Answers 3

1

Well one simple way is to have each UIButton point to the same architectureButtonPressed IBAction method. Since the button that's pressed is passed into the method (sender) you can consult it's tag property to know the index of which field in your struct should be updated. (And then you might want to change your struct to just store an array of 10 bools, but up to you).

Then for each UIButton, whether programmatically in storyboard or nib, you'd assign the appropriate index value to the button's tag field.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, I prefer simple :-) Can I refer to my struct by an index rather than the var name then? The struct is: struct Interests { var architecture: Bool var art: Bool var fashion: Bool var history: Bool var localCulture: Bool var music: Bool var nature: Bool var shopping: Bool var sport: Bool var anything: Bool }
If you find you need to refer to your buttons by index, you can replace your IBOutlet properties of your buttons with a single reference collection. Here's a tutorial.
Thanks, I'll take a look a that and have another go at coding this.
What I'd do is have the struct store an array of bools, and I'd have an enum go with it to define the indexes into your array of bools (architecture = 0, fashion = 1, history = 2, etc...). Also consider looking at Swift enum's associated value capability... you can probably combine it all into a single enum.
Thanks, I'll take a look at that. I didn't mention but I'm going to be passing the struct to the next storyboard as I need it for the next bit of logic (along with some other variables that I'll be passing). As long as I can pass the interests that have been elected by the user to the next stage then that will be ok. By the way, very new to all of this so thanks for the ideas.
|
0
  1. Create yours IBOutlet for each button.

  2. Create a array and store all buttons like : var arrayButtons : [UIButton] = []

    arrayButtons.append[button1]

    arrayButtons.append[button2]

    ...

  3. Create a array of booleans to store true/false: var arrayBools : [Bool] = [] and initialize if some value.

Note that the indexes of the arrayButtons and arrayBools must be same related.

  1. Create selector function to listen touch buttons.

    button.addTarget(self, action: #selector(my_func), for: .touchUpInside)
    
    @objc func my_func(_ sender : UIButton) {
    
    for i in 0...arrayButtons.size-1 {
        if arrayButtons[i] == sender {
            if arrayBooleans[i] {
                arrayBooleans[i] = false
                arrayButtons[i].setImage()
            } else {
                arrayBooleans[i] = true
                arrayButtons[i].setImage()
            }
        }
      }
    }
    

5 Comments

Adding UIButtons (that exist in a view) to an array is dangerous as they now will never be freed unless stored weakly. Whenever Apple decides to free your UIViewController, (which can even potentially happen when you've segue'd from it to another scene), and especially happen if when you've dismissed your view controller, you'll have a memory leak. It's possible to store them as weak references in an Array, but it's some added effort.
You are right, but in this case the most easy way is set weak to outlets. Also, is possible create UIButtons programatically.
If each UIButton is weakly held as an IBOutlet property and then you take the reference and stash it into an Array, I believe you're stashing it strongly (i.e. as a strong reference). Someone correct me if I'm wrong? Also, regarding creating UIButtons programmatically, I think that's orthogonal to the issue because as soon as you stick those UIButtons into the view hierarchy you then still have the strong/weak issue to worry about. Whether the storyboard creates the UIButtons or your code does, it doesn't change the strong retention issue.
Here are some tricks to make weak reference arrays much easier.
I must understand better difference between strong and weak reference.
0

My suggestion is to manage the images in Interface Builder via State Config (Default/Selected)

Then assign an unique tag starting from 100 to each button and set the isSelected value in the IBAction to the corresponding struct member in a switch statement:

@IBAction func buttonPressed(_ sender: UIButton) {
    switch sender.tag {
    case 100: myInterests.architecture = sender.isSelected
    case 101: myInterests.art = sender.isSelected

    ...
    default: break
    }

}

Alternatively use Swift's native KVC with WriteableKeypath

let keypaths : [WritableKeyPath<Interests,Bool>] = [\.architecture, \.art, \.fashion, \.history, \.localCulture, \.music, \.nature, \.shopping, \.sport, \.anything]

@IBAction func buttonPressed(_ sender: UIButton) {
    let index = sender.tag - 100
    let keypath = keypaths[index]
    myInterests[keyPath: keypath] = sender.isSelected
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.