When Should I Use Anyobject Insted of Uibutton in Swift

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’s id type. Swift imports id as AnyObject, 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 of AnyObject 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 on AnyObject.

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



Leave a reply



Submit