I am trying to create an UIAccessibilityCustomAction in Xamarin.iOS. This method require name, target, selector as arguments (as you can see here). The problem is with the selector argument.
In Xcode (using Swift) I can easily implement it like this:
let up = UIAccessibilityCustomAction(name: "Increment", target: self, selector: #selector(increment))
#objc private func increment() -> Bool{
//selector implementation
}
In Xamarin (using C#) I tried that:
UIAccessibilityCustomAction up = new UIAccessibilityCustomAction(name: "Increment", target: iospage, selector: new Selector("Increment"));
And its being said that Selector can take both a String or a IntPtr as argument. Since I have no idea what IntPtr is and how should I use it, I tried to use the String parameter, as you can see above, and I tried to implement the selector like this, following this answer.
[Export("Increment")]
private void Increment()
{
//selector implementation
}
Problem is that seems like this method never got called (i tried to make it log something when the UIAccessibilityCustomAction is called, but no log is shown), probably because it is the wrong way to implement it.
Any idea?
Thanks
UIAccessibilityCustomAction has another instantiate method which you can pass an custom action into it .
UIAccessibilityCustomAction c = new UIAccessibilityCustomAction("Increment",
(UIAccessibilityCustomAction customAction) =>
{
//selector implementation
});
Thanks to the tip of #Cole Xia - MSFT, I found another instantiate method that seems to be easier to handle, using only name, actionHandler, so without a target and selector. actionHandler is easier to use, cause it's only a function that needs a bool return type.
My implementation:
UIAccessibilityCustomAction up = new UIAccessibilityCustomAction("Increment", actionHandler: Increment);
private bool Increment(UIAccessibilityCustomAction customAction)
{
//implementation of the handler
}
Related
This question already has answers here:
Passing closure in swift as parameter to be used by selector in function
(2 answers)
Closed 5 years ago.
I'm attempting to pass in a function to add as a target to a UIButton in Swift3, but I'm getting the following error:
Argument of '#selector' does not refer to an '#objc' method, property, or initializer
Here's my code:
func updateButtonAction(_ action: () -> (), button: UIButton) {
button.addTarget(self, action: #selector(customFunction(action)), for: .touchUpInside)
}
func customFunction(_ function: () -> ()) {
function()
}
I'm wrapping a passed in function in another function so I know the class that owns it.
I think the issue is that first class functions in Swift aren't supported in Objective-C and the selector keyword only uses Objective-C functions.
Does anyone know a way around this?
Selector is just a fancy synonym for method name. It's not a closure or a function. It's the name of a method on an Obj-C object. Technically, a selector is a string.
customFunction(action) is not a selector. The selector is customFunction(_:).
You are trying to pass an implicit parameter to the selector. That's not how selectors work. You are not calling the method, you are just telling the UIButton which method it should call - UIButton is calling the method and the button is also specifying the parameters.
If you need to pass some code to the event handler, save it on your instance.
var function: (() -> Void)?
func customFunction() {
self.function?()
}
In general it's a good idea to have only one event handler and not change it.
Also note that your example has no need for first class functions.
[NOTE This question was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]
Let's say I have these two methods in my class:
#objc func test() {}
#objc func test(_ sender:AnyObject?) {}
Now I want to use Swift 2.2's new #selector syntax to make a selector corresponding to the first of these methods, func test(). How do I do it? When I try this:
let selector = #selector(test) // error
... I get an error, "Ambiguous use of test()." But if I say this:
let selector = #selector(test(_:)) // ok, but...
... the error goes away, but I'm now referring to the wrong method, the one with a parameter. I want to refer to the one without any parameter. How do I do it?
[Note: the example is not artificial. NSObject has both Objective-C copy and copy: instance methods, Swift copy() and copy(sender:AnyObject?); so the problem can easily arise in real life.]
[NOTE This answer was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]
You can work around this problem by casting your function reference to the correct method signature:
let selector = #selector(test as () -> Void)
(However, in my opinion, you should not have to do this. I regard this situation as a bug, revealing that Swift's syntax for referring to functions is inadequate. I filed a bug report, but to no avail.)
Just to summarize the new #selector syntax:
The purpose of this syntax is to prevent the all-too-common runtime crashes (typically "unrecognized selector") that can arise when supplying a selector as a literal string. #selector() takes a function reference, and the compiler will check that the function really exists and will resolve the reference to an Objective-C selector for you. Thus, you can't readily make any mistake.
(EDIT: Okay, yes you can. You can be a complete lunkhead and set the target to an instance that doesn't implement the action message specified by the #selector. The compiler won't stop you and you'll crash just like in the good old days. Sigh...)
A function reference can appear in any of three forms:
The bare name of the function. This is sufficient if the function is unambiguous. Thus, for example:
#objc func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test)
}
There is only one test method, so this #selector refers to it even though it takes a parameter and the #selector doesn't mention the parameter. The resolved Objective-C selector, behind the scenes, will still correctly be "test:" (with the colon, indicating a parameter).
The name of the function along with the rest of its signature. For example:
func test() {}
func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test(_:))
}
We have two test methods, so we need to differentiate; the notation test(_:) resolves to the second one, the one with a parameter.
The name of the function with or without the rest of its signature, plus a cast to show the types of the parameters. Thus:
#objc func test(_ integer:Int) {}
#nonobjc func test(_ string:String) {}
func makeSelector() {
let selector1 = #selector(test as (Int) -> Void)
// or:
let selector2 = #selector(test(_:) as (Int) -> Void)
}
Here, we have overloaded test(_:). The overloading cannot be exposed to Objective-C, because Objective-C doesn't permit overloading, so only one of them is exposed, and we can form a selector only for the one that is exposed, because selectors are an Objective-C feature. But we must still disambiguate as far as Swift is concerned, and the cast does that.
(It is this linguistic feature that is used — misused, in my opinion — as the basis of the answer above.)
Also, you might have to help Swift resolve the function reference by telling it what class the function is in:
If the class is the same as this one, or up the superclass chain from this one, no further resolution is usually needed (as shown in the examples above); optionally, you can say self, with dot-notation (e.g. #selector(self.test), and in some situations you might have to do so.
Otherwise, you use either a reference to an instance for which the method is implemented, with dot-notation, as in this real-life example (self.mp is an MPMusicPlayerController):
let pause = UIBarButtonItem(barButtonSystemItem: .pause,
target: self.mp, action: #selector(self.mp.pause))
...or you can use the name of the class, with dot-notation:
class ClassA : NSObject {
#objc func test() {}
}
class ClassB {
func makeSelector() {
let selector = #selector(ClassA.test)
}
}
(This seems a curious notation, because it looks like you're saying test is a class method rather than an instance method, but it will be correctly resolved to a selector nonetheless, which is all that matters.)
I want to add a missing disambiguation: accessing an instance method from outside the class.
class Foo {
#objc func test() {}
#objc func test(_ sender: AnyObject?) {}
}
From the class' perspective the full signature of the test() method is (Foo) -> () -> Void, which you will need to specify in order to get the Selector.
#selector(Foo.test as (Foo) -> () -> Void)
#selector(Foo.test(_:))
Alternatively you can refer to an instance's Selectors as shown in the original answer.
let foo = Foo()
#selector(foo.test as () -> Void)
#selector(foo.test(_:))
In my case (Xcode 11.3.1) the error was only when using lldb while debugging. When running it works properly.
The full test case below is supposed to demonstrate: a selector, even though it is specified identically in two places, is performed differently: either it is performed on the class, or on the object. (I understand that a static method and an object method can share the same name, but there is only one below.) Whether the receiver is class or object seems to depend on where the “same” selector is made known to NSNotificationCenter, either in class context or in method context:
a static method has the call to addObserver, or
an object method has the call addObserver
while the calls are otherwise identical.
If the identical call occurs in a static method, then when the notification is processed later, the system tries to invoke the selector on the class, not the object. The class does not have it. The code compiles fine with the new (in 2.2) syntax. Is this result to be expected?
import XCTest
import class Foundation.NSNotificationCenter // for emphasis
class SelectorTests: XCTestCase {
static let NotificationName = "OneTwoThreeNotification"
override func setUp() {
super.setUp()
}
override func tearDown() {
NSNotificationCenter.defaultCenter().removeObserver(self)
super.tearDown()
}
func addObserverForTestNormal() { // <- HERE
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(SelectorTests.myMethod(_:)), // <- HERE
name: SelectorTests.NotificationName,
object: nil)
}
func testNormal() {
self.addObserverForTestNormal()
NSNotificationCenter.defaultCenter().postNotificationName(
SelectorTests.NotificationName,
object: self)
}
static func addObserverForTestStatic() { // <- HERE
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(SelectorTests.myMethod(_:)), // <- HERE
name: SelectorTests.NotificationName,
object: nil)
}
func testStatic() {
SelectorTests.addObserverForTestStatic()
NSNotificationCenter.defaultCenter().postNotificationName(
SelectorTests.NotificationName,
object: self)
}
func myMethod(x : Int) {
XCTAssert(true)
}
}
One test succeeds, the other fails. The gist of the stack trace and message is
"+[KuckuckTests.SelectorTests myMethod:]: unrecognized selector sent to class
Is this schism, i.e. class or object “inferred” from addObserver-context, so obvious to old Objective-C hands that it isn't worth mentioning with #selector? In this case, could you point out some documentation?
Edit: just noticed that self in the static function's invocation
of addObserver is perhaps referring to the class, not to some object. That makes the effect somewhat plausible, and suggests that programmers should know what overloaded names stand for…
Nothing about a #selector expression has any connection to the use site of that selector. A selector names a message, and says nothing about the receiver of that message. You can use a #selector expression to create a Selector value for a method on one object, then pass that Selector value to an API (like NSNotificationCenter.addObserver or UIControl.sendAction or NSTimer.init) that'll result in sending a message with that selector to some completely different object.
This loose binding is an intentional part of the dynamic nature of the Objective-C runtime Cocoa uses for passing these messages (regardless of whether the functions referenced by your selectors are build in ObjC or Swift). The #selector expression, and the Swift function-reference syntax it depends on, give you a way to "sorta" strongly type your use of selectors, but only on one end — they let you verify that the Selector value you're constructing refers to a specific method. (But once you have a Selector value, how it gets used is out of Swift's control.)
Your error message (emphasis added):
unrecognized selector sent to class
...indicates that the failure is because the message is being sent to the SelectorTests class object (aka the metaclass object). That is, by scheduling a notification to be sent to self in a static method, you're asking for a call to class func myMethod, not to func myMethod.
The self keyword always refers to the instance responsible for the code that's executing: inside an instance method, self refers to the current instance. Inside a class method, self refers to the (only instance of) the class object.
I have the following code:
func setupShortcutItems(launchOptions: [NSObject: AnyObject]?) -> Bool {
var shouldPerformAdditionalDelegateHandling: Bool = false
if (UIApplicationShortcutItem.respondsToSelector("new")) {
self.configDynamicShortcutItems()
// If a shortcut was launched, display its information and take the appropriate action
if let shortcutItem: UIApplicationShortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
// When the app launched at the first time, this block can not called.
self.handleShortCutItem(shortcutItem)
// This will block "performActionForShortcutItem:completionHandler" from being called.
shouldPerformAdditionalDelegateHandling = false
} else {
// normal app launch process without quick action
self.launchWithoutQuickAction()
}
} else {
// Less than iOS9 or later
self.launchWithoutQuickAction()
}
return shouldPerformAdditionalDelegateHandling
}
I get the following "warning" on UIApplicationShortcutItem.respondsToSelector("new"), which says:
Use of string literal for Objective-c selectors is deprecated, use '#selector' instead
The warning replaces the code automatically with:
UIApplicationShortcutItem.respondsToSelector(#selector(FBSDKAccessToken.new))
However this doesn't compile because new() is unavailabe.
What am I supposed to use in this case?
Xcode 7.3 using swift for iOS9.3/watchOS2.2/...
If you previously used this line of code:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateResult:", name: "updateResult", object: nil)
you should now use this line of code:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(InterfaceController.updateResult(_:)), name: "updateResult", object: nil)
at least this is what Xcode offered me after I changed around a few characters in the code. Seems like it doesn't always offer the correct solution when you are presented with this error.
Create a protocol whose only reason for existing is to allow you to construct the appropriate selector. In this case:
#objc protocol NewMenuItemHandling {
func new()
}
You are taking the informal protocol (an object that responds to the new selector) and making it into a formal protocol.
Then where you want to use the selector you can add the expression:
#selector(NewMenuItemHandling.new)
In this special respondsToSelector situation, where you have no existing method with which to associate a function reference, write this:
UIApplicationShortcutItem.respondsToSelector(Selector("new"))
You'll still get a warning (you shouldn't, and I've filed a bug against it), but it will be a different warning and you can ignore it.
In summary, every "selector: function or object" is now "selector: #selector(class.funtion(_:))" wherever used.
Here is working code which runs fine in Objective-C,
[dataFetcher fetchDataWithRequest:authorizeRequest
delegate:self
didFinishSelector:#selector(didReceiveRequestToken:data:)
didFailSelector:#selector(didFailOAuth:error:)];
It is calling both methods in Objective-C.
But when I use Selector in Swift, it doesn't work.
dataFetcher?.fetchDataWithRequest(requestTokenRequest,
delegate: self,
didFinishSelector:Selector("didReceiveRequestToken:data:"),
didFailSelector: Selector("didFailOAuth:error:"))
Here the method which i'm calling,
func didReceiveRequestToken(ticket:OAServiceTicket,data:NSData){
}
I've already use solutions given on StackOverFlow but they didn't worked for me.
Thanks in Advance.
It`s good a bit change in your method:
func didReceiveRequestToken(ticket:OAServiceTicket,andData data:NSData){
}
The selector is: "didReceiveRequestToken:andData:"
Your call:
dataFetcher?.fetchDataWithRequest(requestTokenRequest,
delegate: self,
didFinishSelector:"didReceiveRequestToken:andData:",
didFailSelector:"didFailOAuth:andError:") // Make the same with this.
Be sure your code have this method.
Possibly, you are forgetting to use #objc in front of your method implementations:
#objc func didReceiveRequestToken(ticket:OAServiceTicket,data:NSData) {
....
This is required when the the target method is not defined inside of a class derived from NSObject (i.e., with pure-Swift classes).