Search code examples
iosswiftswitch-statementibaction

How can I change the action of an IBAction button using a swift statement correctly?


I brought this up in one of my other questions, but I'm working on a text based adventure game designed for IOS.

One of the things I'm working on is to give a button multiple different functions under specific circumstances. I read in a handful of other posts that I can achieve this by using swift statements and I have been somewhat successful in changing the action, however it is not the correct action.

As shown below most of my story, and what options the player is given, are stored in a structure, and can be switched using the PickStory method.

func mainStory()
{
    Storys =
        [Story(Story: "You are walking along a dirt path and come to a cross roads. You see a small shack just off the trail. What do you want to do?", Action: "Heal", North: true, South: true, East: false, West: false, storyProg: true, resultN: "You walk north and see a gate in the distance.", resultS: "Their is nothing to turn back for.", resultE: "", resultW: ""),

         Story(Story: "You see a small gate at the end of the dirt path, and their is a gaurd standing infront of the gate.", Action: "Approach", North: true, South: true, East: true, West: true, storyProg: false, resultN: "", resultS: "", resultE: "", resultW: ""),

         Story(Story: "You see a small well in the middle of town.", Action: "Attack", North: true, South: true, East: true, West: true, storyProg: false, resultN: "", resultS: "", resultE: "", resultW: "")]


    PickStory()
}

func PickStory()
{
    if Storys.count > 0
    {
        storyNum = 0
        storyLabel.text = Storys[storyNum].Story
        actionButton.setTitle(Storys[storyNum].Action, for: UIControl.State.normal)

        adjustButtons(North: Storys[storyNum].North,
                      South: Storys[storyNum].South,
                      East: Storys[storyNum].East,
                      West: Storys[storyNum].West)

        Storys.remove(at: storyNum)
    }
    else
    {
        NSLog("Done!")
    }
}

Now while the text of the Action button is established in the PickStory method, the actual actions are changed in the actual button method that follows a few lines after (Please note that the print statements are only temporary place holders for the methods that will be placed later).

@IBAction func actionButton(_ sender: Any)
{
    switch Storys[storyNum].Action
    {
        case "Attack":
            print("Attacking")
            break
        case "Heal":
            print("Healing")
            break
        case "Approach":
            print("Approaching")
            break
    default:
        break
    }
}

To summarize the problem, the text will change to the correct action, but the actual action won't change.

My original guess was that because the actionButton comes a while later, after the PickStory method, it would read the next story after the index was removed. However, I cannot get any progress through the story without removing indexes.


Solution

  • You cannot send parameters through a selector action, but you could subclass the UIButton to add a custom property, which can be anything.

    import UIKit
    
    class CustomButton: UIButton {
        var storyAction: String
    
        override init(frame: CGRect) {
            self.storyAction = ""
            super.init(frame: frame)
        }
    
        required init?(coder aDecoder: NSCoder) {
            self.storyAction = ""
            super.init(coder: aDecoder)
        }
    }
    

    You can set this property when you set the title:

    let action = Storys[storyNum].action // Customary to use lower case for instance properties and upper case for classes
    actionButton.setTitle(action, for: UIControl.State.normal)
    actionButton.storyAction = action
    

    You can check it in the switch statement.

    @IBAction func actionButton(_ sender: CustomButton)
    {
        switch sender.storyAction
        {
            case "Attack":
                print("Attacking")
                break
            case "Heal":
                print("Healing")
                break
            case "Approach":
                print("Approaching")
                break
        default:
            break
        }
    }
    

    Note that I called the property storyAction so as not to conflict with the button’s pre-existing action property.


    It might be safer to use an enum for all your storyAction types, instead of strings. This would make it easier to ensure there were no spelling mistakes causing problems!

    enum StoryAction {
        case attack
        case heal
        case approach
    }
    

    This can be extended as much as you need. It is easy to check in a switch statement with case .attack etc.