P.Angle
P.Angle

Reputation: 21

In Swift 3.0, how to check what button was pressed

I understand that the following will return the title of a button that was pressed.

let myString = sender.title(for: .normal)!

But I would really like to understand why this works. I could not find it in Apple documentation for UIButton.

I read in another thread that sender is a parameter of type Any rather than UIButton, but I would appreciate any help in deciphering this syntax.

Upvotes: 2

Views: 4153

Answers (3)

JAB
JAB

Reputation: 3235

You can see the type of sender in the method name. The code you posted will only work if sender is a UIButton, like in the following:

@IBAction func sampleMethod(_ sender: UIButton) {
    let myString = sender.title(for: .normal)
}

If sender is of type Any, then you must unwrap it as a UIButton in order to access the title the same way, like so:

@IBAction func sampleMethod(_ sender: Any) {
    if let sender = sender as? UIButton {
        let myString = sender.title(for: .normal)
    }
}

If you don't do this, the app won't compile. Type Any doesn't have a title property.

If you are creating an IBAction function by dragging from a storyboard to code, the default is to have the sender type be Any, as you can see here:

enter image description here

You can set it to UIButton by using the drop down menu next to Type, like so:

enter image description here

Upvotes: 4

Dhruv Khatri
Dhruv Khatri

Reputation: 813

 guard let string = ((sender) as AnyObject).title(for: .normal)
    else{
        return
    }
    print(string)

This will also work.(If you don't know the type of sender)

But most common way for button is

let string = (sender as! UIButton).title(for: .normal)
print(str!)

Upvotes: 0

Paulw11
Paulw11

Reputation: 114826

The Target-Action mechanism is described in the documentation for the UIControl class, which is class that actionable controls, such as UIButton, inherit from.

Essentially, the action function must implement one of the following signatures:

  • doSomething()
  • doSomething(_ sender: Any)
  • doSomething(_ sender: Any, event: UIEvent)

The sender parameter will be the control that sent the action.

Any in Swift is a special type that can represent any type but it is not an actual type, so if the sender is a UIButton then sender will be an instance of UIButton, but you can't access any of it's specific methods or properties unless you downcast it. You could say something like:

func doSomething(_ sender: Any) {
    if let button = sender as? UIButton {
          let myString = button(for: .normal)
}

However, generally you know how you have connected your controls and action handlers, so there is no need to conditionally downcast; you can use an explicit downcast:

func doSomething(_ sender: Any) {
    let button = sender as! UIButton 
    let myString = button(for: .normal)
}

Or, even more succinctly:

func doSomething(_ sender: UIButton) {
    let myString = button(for: .normal)
}

Now, if you mistakenly connected doSomething to an action on a text field, these last two forms would give you a runtime crash as the downcast fails; but hopefully you catch this during testing and so it is generally considered "safe" to do.

Upvotes: 1

Related Questions