When should I use anyObject insted of UIButton in swift?
Ultimately, it really doesn't matter.
You can choose to use the parameter of (sender: AnyObject)
or you can use (sender: UIButton)
.
There might be times however where you might have to cast AnyObject
as a UIButton
if you need access to the properties provided by UIButton
.
For example let's say you have and you want the button to disappear after it is clicked.
func doSomething(sender: AnyObject) {
let button: UIButton = sender as! UIButton
button.hidden = true
}
What's the difference between AnyObject and UIbutton as sender?
Yes, both works. The difference is just that by declaring it to a button, you get a typecasted reference as UIButton instead of AnyObject (or id in Objective-C). If you did not do that you will have to do it manually inside the code.
You should prefer leaving it to AnyObject in case the action has some code you would like to call from anywhere else, rather than just the button action.
For example a refresh button, you might have to do a refresh programatically. If you have set your action parameter to UIButton, you have to send an UIButton (but why??? You are doing it programatically, right?). Whereas if it is AnyObject, you can send 'self'. Which will also make you distinguish whether the refresh was done on action or programatically.
Hope that explains.
Sorry for no code. I am not into Swift coding much. Edits are welcome.
EDIT
So, even if it is AnyObject, it does have your UIButton properties. Just type cast it to use them.
Swift / iOS - UIButton Sender with more information
You can certainly do that, and sometimes it makes for a clean design. View objects have a tag property (an Int.) You can put a switch statement on your action method(s) that switches on the tag value. Generally you should use tag values starting from 1 or greater, since 0 is the default tag value.
If you need more than an integer value you can either save your values into an array that you index into with the tag value or you can use associative storage to actually store objects attached to your buttons. Do a search on "Cocoa associated objects" for more information on that technique. Or you can take a look at a demo project I put on github: https://github.com/DuncanMC/AssocitiveStorageDemo (Written in Objective-C, but the technique is the same in Swift)
Switching on UIButton title: Expression pattern of type 'String' cannot match values of type 'String?!'
currentTitle
is an optional so you need to unwrap it. Also, the type of sender
should be UIButton
since you are accessing the currentTitle
property.
@IBAction func buttonClick(sender: UIButton) {
if let theTitle = sender.currentTitle {
switch theTitle {
case "Button1":
print("Clicked Button1")
case "Button2":
print("Clicked Button2")
default:
break
}
}
}
UIButton as UIButton in swift?
You need to keep the as UIButton
downcast. buttonWithType()
return AnyObject!
, not UIButton
, so the downcast is necessary. Other than that, you don't need to explicitly type the variable with : UIButton
. Since the return type of buttonWithType()
as downcast to UIButton, the variables type will be inferred to be UIButton
. This is what you should use:
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
The strange behaviour of Swift's AnyObject
Similar as in Objective-C, where you can send arbitrary messages to id
,
arbitrary properties and methods can be called on an instance of AnyObject
in Swift. The details are different however, and it is documented in
Interacting with Objective-C APIs
in the "Using Swift with Cocoa and Objective-C" book.
Swift includes an
AnyObject
type that represents some kind of object. This is similar to Objective-C’sid
type. Swift importsid
asAnyObject
, which allows you to write type-safe Swift code while maintaining the flexibility of an untyped object.
...
You can call any Objective-C method and access any property on an AnyObject value without casting to a more specific class type. This includes Objective-C compatible methods and properties marked with the@objc
attribute.
...
When you call a method on a value ofAnyObject
type, that method call behaves like an implicitly unwrapped optional. You can use the same optional chaining syntax you would use for optional methods in protocols to optionally invoke a method onAnyObject
.
Here is an example:
func tryToGetTimeInterval(obj : AnyObject) {
let ti = obj.timeIntervalSinceReferenceDate // NSTimeInterval!
if let theTi = ti {
print(theTi)
} else {
print("does not respond to `timeIntervalSinceReferenceDate`")
}
}
tryToGetTimeInterval(NSDate(timeIntervalSinceReferenceDate: 1234))
// 1234.0
tryToGetTimeInterval(NSString(string: "abc"))
// does not respond to `timeIntervalSinceReferenceDate`
obj.timeIntervalSinceReferenceDate
is an implicitly unwrapped optional
and nil
if the object does not have that property.
Here an example for checking and calling a method:
func tryToGetFirstCharacter(obj : AnyObject) {
let fc = obj.characterAtIndex // ((Int) -> unichar)!
if let theFc = fc {
print(theFc(0))
} else {
print("does not respond to `characterAtIndex`")
}
}
tryToGetFirstCharacter(NSDate(timeIntervalSinceReferenceDate: 1234))
// does not respond to `characterAtIndex`
tryToGetFirstCharacter(NSString(string: "abc"))
// 97
obj.characterAtIndex
is an implicitly unwrapped optional closure. That code
can be simplified using optional chaining:
func tryToGetFirstCharacter(obj : AnyObject) {
if let c = obj.characterAtIndex?(0) {
print(c)
} else {
print("does not respond to `characterAtIndex`")
}
}
In your case, TestClass
does not have any @objc
properties.
let xyz = typeAnyObject.xyz // error: value of type 'AnyObject' has no member 'xyz'
does not compile because the xyz
property is unknown to the compiler.
let name = typeAnyObject.name // String!
does compile because – as you noticed – NSException
has a name
property.
The value however is nil
because TestClass
does not have an
Objective-C compatible name
method. As above, you should use optional
binding to safely unwrap the value (or test against nil
).
If your class is derived from NSObject
class TestClass : NSObject {
var name : String?
var xyz : String?
}
then
let xyz = typeAnyObject.xyz // String?!
does compile. (Alternatively, mark the class or the properties with @objc
.)
But now
let name = typeAnyObject.name // error: Ambigous use of `name`
does not compile anymore. The reason is that both TestClass
and NSException
have a name
property, but with different types (String?
vs String
),
so the type of that expression is ambiguous. This ambiguity can only be
resolved by (optionally) casting the AnyObject
back to TestClass
:
if let name = (typeAnyObject as? TestClass)?.name {
print(name)
}
Conclusion:
- You can call any method/property on an instance of
AnyObject
if that
method/property is Objective-C compatible. - You have to test the implicitly unwrapped optional against
nil
or
use optional binding to check that the instance actually has that
method/property. - Ambiguities arise if more than one class has (Objective-C) compatible
methods with the same name but different types.
In particular because of the last point, I would try to avoid this
mechanism if possible, and optionally cast to a known class instead
(as in the last example).
Adding a closure as target to a UIButton
Do Not Use This Answer, See Note Below
NOTE:
like @EthanHuang said
"This solution doesn't work if you have more than two instances. All actions will be overwrite by the last assignment."
Keep in mind this when you develop, i will post another solution soon.
If you want to add a closure as target to a UIButton
, you must add a function to UIButton
class by using extension
Swift 5
import UIKit
extension UIButton {
private func actionHandler(action:(() -> Void)? = nil) {
struct __ { static var action :(() -> Void)? }
if action != nil { __.action = action }
else { __.action?() }
}
@objc private func triggerActionHandler() {
self.actionHandler()
}
func actionHandler(controlEvents control :UIControl.Event, ForAction action:@escaping () -> Void) {
self.actionHandler(action: action)
self.addTarget(self, action: #selector(triggerActionHandler), for: control)
}
}
Older
import UIKit
extension UIButton {
private func actionHandleBlock(action:(() -> Void)? = nil) {
struct __ {
static var action :(() -> Void)?
}
if action != nil {
__.action = action
} else {
__.action?()
}
}
@objc private func triggerActionHandleBlock() {
self.actionHandleBlock()
}
func actionHandle(controlEvents control :UIControlEvents, ForAction action:() -> Void) {
self.actionHandleBlock(action)
self.addTarget(self, action: "triggerActionHandleBlock", forControlEvents: control)
}
}
and the call:
let button = UIButton()
button.actionHandle(controlEvents: .touchUpInside,
ForAction:{() -> Void in
print("Touch")
})
Swift unable to connect multiple buttons via IBAction Any but okay via AnyObject
Last things first :)
The Difference
If you look at the description of Any
and AnyObject
in The Swift Programming Language you'll find this description:
Any can represent an instance of any type at all, including function types.
AnyObject can represent an instance of any class type.
Why this behaviour
I'm guessing here but @IBAction
is part of the Target-Action pattern used by Apple for communicating between different elements of your app (targets know how to relate to actions). As this pattern predates Swift I'm guessing the sender
of an action has to be a class for this to work. In Objective C you would use id
, meaning "Any Class", and in Swift - as we've just found out - the representation of "Something that can be of type any class" is AnyObject
.
Hope that makes sense and helps you.
Related Topics
iOS Pod Install Gcm and Pnchartswift
In Swift, Can a Function Be a Type
Uitableview Inside Uitableviewcell with Dynamic Height
Xcode 12.5 Missing Entitlement Com.Apple.Developer.Associated-Appclip-App-Identifiers
How Posting One Signal Notification's Additional Data and Receiving That
Diffabledatasource: Snapshot Doesn't Reload Headers & Footers
Giving Properties of a Uibutton to a Skspritenode in Spritekit
Start/Stop Image View Rotation Animations
How to Call Rating View from JSON and Show the Rating in My Table View
Timer Label Not Updated After Switching Views (Swift)
How Would I Store a Video into Firebase Storage from Swift
How to Manage and Free Memory Through Viewcontrollers
Design Pattern for Realm Database Swift 3.1 - Singleton
Swift 3 - How to Improve Image Quality for Tesseract
Changing Font Size in a Label for Only iPhone 4S, Is This Possible
Differrence Between Closure and Function as Argument in Swift