The #selector is not compatible with the closure?
A selector is a string that's used to identify methods, properties, initializers in the Objective C runtime. When you use a notation like #selector(SomeClass.SomeMethod(withParam:AndParam:)
, you're specifying the selector in a format that the compiler can parse easily and verify to be correct. But ultimately, that would just be reduced down into a C string like: "SomeMethodwithParam:AndParam:"
.
Essentially, every class has a dictionary which maps selectors to the function pointers of the code that implements them. When a selector is used to invoke a function, the Objective C runtime searches the method table for the class in question, and looks up the method implementation corresponding to the given selector.
There's no way for this process to work with closures, which are anonymous by definition. Thus, you can only use selectors to refer to methods, properties, initializers that are registered with the Objective C runtime (which is what @objc
does, implicitly or explicitly).
Can I make #selector refer to a closure in Swift?
As @gnasher729 notes, this is not possible because selectors are just names of methods, not methods themselves. In the general case, I'd use dispatch_after
here, but in this particular case, the better tool IMO is UIView.animateWithDuration
, because it's exactly what that function is for, and it's very easy to tweak the transition:
UIView.animateWithDuration(0, delay: 0.5, options: [], animations: {
self.view.backgroundColor = UIColor.whiteColor()
self.view.alpha = 1.0
}, completion: nil)
Closures for UIGestureRecognizers
Your problem is your target is on on the TapGestureRecognizer
class itself. Your runAction
method is an instance method, but you're telling the super.init
that it's a class method, thus the failure here: +[MyApp.TapGestureRecognizer runAction]
. You can fix this by making runAction
a class method.
You could also remove the target on class and add the instance after initialization:
init() {
super.init(target: TapGestureRecognizer.self, action: #selector(runAction))
self.removeTarget(TapGestureRecognizer.self, action: #selector(runAction))
self.addTarget(self, action: #selector(runAction))
}
Or you could somehow wrap the closure to be fired in a different object.
Swift Selectors and Closures Discussion
In your case at the moment, it seems that no cyclic-reference is formed. But it's possible to accidentally be formed in the future. Like the followings for the instance.
// In the `Test` class.
let control = MyControl()
init() {
control.addClosureFor(UIControlEvents.TouchUpInside, closure: { (control) -> () in
self.testProperty = "This is making a reference cycle?"
})
}
In this code, self
has a reference to the control
as its field and the control
has a reference to self
through the closure added by addClosureFor
. This forms a cyclic reference so these objects in the cycle are never released.
closure capture list helps this issue.
control.addClosureFor(UIControlEvents.TouchUpInside) { [weak self] /* <-- */ control -> () in
// Here, `self` is visible as `Optional<Test>` so the `?` operator is required.
self?.testProperty = "This is making a reference cycle?"
return
}
In the block in this code above, self
is weakly captured so that an only one unidirectional reference is formed from self
to the control
.
Note that, now in the closure, the access to self
isn't assured because self
is weakly-referenced. You have to consider self
an optional value.
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
How to use the not selector correctly in CSS
Your only issue is that you're passing two selectors inside the :not()
use only one per statement.
Currently extended arguments (foo, bar)
are not supported by any browser.
Use
:not(.class1):not(.class2)
https://developer.mozilla.org/en-US/docs/Web/CSS/%3Anot
input[type=text][readonly]:not(.class1):not(.class2) { background-color: #DDDDDD; color: #1E1E1E;}
<input type=text readonly class="class1"><input type=text readonly><input type=text readonly class="class2">
Argument of '#selector' does not refer to an '@objc' method, property or initializer
The selector of a target / action method must be declared either without parameter or with one parameter passing the affected object.
In case of a Timer
use the userInfo
parameter to pass data.
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector:#selector(updateTimer(_:)), userInfo: 3, repeats: true)
func updateTimer(_ timer: Timer) {
let endTime = timer.userInfo as! Int
counter -= 1
timeLabel.text = String(counter)
if counter == endTime {
step += 1
}
}
If the enclosing class does not inherit form NSObject
you have to add the @objc
attribute to the action method.
Related Topics
Easiest Way to Truncate Float to 2 Decimal Places
Nil Cannot Be Assigned to Type Avcapturedeviceinput
Swift System Version Checking on Ubuntu
Where Do I Register a Valuetransformer in Swift
Uidatepicker Show Only Sunday's Date Only
How to Create a Cocoapod with .Swift
Apple Watch and iPhone Are Not Connected When The App in Phone Goes to Background
Realm: Predicate Returning Lazyfiltercollection - How to Convert to Results<T>
Swift Combine How Set<Anycancellable> Works
Destructuring Tuple of Tuple in Closure
How to Put View on Top of All Other Views in Swiftui
Using Multiple Hosting Controllers in Swiftui on Watchos
Dependency Injection in View Controller
Is There Any Difference at All Between Suffix(From:) and Dropfirst(_:)