Reputation: 21
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
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:
You can set it to UIButton
by using the drop down menu next to Type, like so:
Upvotes: 4
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
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