1

I'm trying to call a function with parameters on a button press, from what I understand #selector just checks that a function is there and then runs it. I've seen other answers where the button can be sent to the function but sadly I don't think that will solve my problem.

If I run this code (func is any function and a: 14 is just an example of a parameter being given):

myButton.addTarget(self, action: #selector(func(a: 14)), for: .touchUpInside)

I get an error saying 'Argument of #selector does not refer to an @objc method, property, or initializer

A workaround that I've been using:

myButton.addTarget(target: self, action: #selector(myFunc), for: .touchUpInside)

func myFunc() {
 someOtherFunc(args)
}

The problem with this is that unless the argument that was going to be passed is global or class wide and known, you wont be able to use it.

Main Question: Is there a way to have it run a function with parameters when a button is clicked without setting a class wide variable and assessing that instead of using a parameter?

My Solution: Simplified, and buttons aren't setup and such...

var personName:String!

func scrollViewClicked(name:String) {

   personName = name 

   myButton.addTarget(target: self, action: #selector(myFunc), for: .touchUpInside)

}

func myFunc() {
   do something with personName
}

So pretty much I have a way of 'solving' the problem but it feels like a bit of a hack/improper way. Just trying to figure out if there is a 'real' way to do this or if it isn't meant to happen.

Rawbeef
  • 21
  • 4
  • Definition of the function you are calling??? – Hobbit May 17 '17 at 23:56
  • From what I understand you can't. At least you couldn't back in Swift 2. I've been hacking around the limitations the same way you have. There are a lot of similar questions out there. Here's one http://stackoverflow.com/a/38329101/5378116 – Pierce May 18 '17 at 00:03
  • 1
    It's *not* Swift, it's how **iOS** works. You have "outlets" that may have "actions". These *actions* have a sender - a control. In one respect, this is MVC at it's most "pure". (And yes, on the other hand, it's not connected like you may be used to.) Deal with it! –  May 18 '17 at 00:18
  • @Umer any function as long as it takes a parameter – Rawbeef May 18 '17 at 00:44
  • @Pierce that's what my example has however I assumed that this was a work around. I was wondering if there was a cleaner way – Rawbeef May 18 '17 at 00:45

1 Answers1

1

No, there's no way to have a UIButton run a function with arbitrary parameters.

But there is a way to have it run with some parameters, which may be useful to you.

The documentation for addTarget says that it takes a selector, which is essentially just a reference to a method. If you pass it a method with the right set of arguments, it will call it and pass whatever it's designed to pass. If you send a method with other arguments, you'll get an "unrecognized selector" error.

UIControl's addTarget understands three kinds of selectors:

func myFunc()
func myFunc(sender: UIButton)
func myFunc(sender: UIButton, forEvent event: UIEvent)

So you can set it to run a function with parameters, but the only parameters it knows how to send are the button that was pressed and the event it generated.

This is still potentially useful though, if you can use information about the button and/or the event to determine your action. For example you can set up your handler:

func myFunc(sender: UIButton, forEvent event: UIEvent) {
    switch(sender) {
    case myButton:
        print("myButton was pressed")
    default:
        print("Something else was pressed.")
    }
}

Depending on your use case, you could make use of the button's storyboard restoration ID, its title or other identifier, or you could even subclass UIButton and give it an instance variable to hold your parameter, like this:

class MyButtonClass: UIButton {
    var argument:String = ""
}

Then when you're setting up your button you specify the argument:

myButton.argument = "Some argument"

And you can access it from your handler like this:

func myFunc(sender: UIButton, forEvent event: UIEvent) {
    if let button = sender as? MyButtonClass {
        print(button.argument)
    }
}

It's still not as neat as just specifying your parameter in the selector, but as far as I know that's not possible.

Robert
  • 6,660
  • 5
  • 39
  • 62