Echizzle
Echizzle

Reputation: 3459

'#selector' refers to a method that is not exposed to Objective-C

The new Xcode 7.3 passing the parameter via addTarget usually works for me but in this case it's throwing the error in the title. Any ideas? It throws another when I try to change it to @objc

Thank you!

cell.commentButton.addTarget(self, action: #selector(FeedViewController.didTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)

The selector it's calling

func didTapCommentButton(post: Post) {
}

Upvotes: 110

Views: 67342

Answers (4)

yoAlex5
yoAlex5

Reputation: 34235

As you know selector[About] says that Objective-C runtime[About] should be used. Declarations that are marked as private or fileprivate are not exposed to the Objective-C runtime by default. That is why you have two variants:

  1. Mark your private or fileprivate method declaration by @objc[About]
  2. Use internal, public, open method access modifier[About]

Upvotes: 0

Shaked Sayag
Shaked Sayag

Reputation: 5792

In my case the function of the selector was private. Once I removed the private the error was gone. Same goes for fileprivate.

In Swift 4
You will need to add @objc to the function declaration. Until swift 4 this was implicitly inferred.

Upvotes: 180

pfj
pfj

Reputation: 187

Try having the selector point to a wrapper function, which in turn calls your delegate function. That worked for me.

cell.commentButton.addTarget(self, action: #selector(wrapperForDidTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)

-

func wrapperForDidTapCommentButton(post: Post) {
     FeedViewController.didTapCommentButton(post)
}

Upvotes: 8

rob mayoff
rob mayoff

Reputation: 385600

You need to use the @objc attribute on didTapCommentButton(_:) to use it with #selector.

You say you did that but you got another error. My guess is that the new error is that Post is not a type that is compatible with Objective-C. You can only expose a method to Objective-C if all of its argument types, and its return type, are compatible with Objective-C.

You could fix that by making Post a subclass of NSObject, but that's not going to matter, because the argument to didTapCommentButton(_:) will not be a Post anyway. The argument to an action function is the sender of the action, and that sender will be commentButton, which is presumably a UIButton. You should declare didTapCommentButton like this:

@objc func didTapCommentButton(sender: UIButton) {
    // ...
}

You'll then face the problem of getting the Post corresponding to the tapped button. There are multiple ways to get it. Here's one.

I gather (since your code says cell.commentButton) that you're setting up a table view (or a collection view). And since your cell has a non-standard property named commentButton, I assume it's a custom UITableViewCell subclass. So let's assume your cell is a PostCell declared like this:

class PostCell: UITableViewCell {
    @IBOutlet var commentButton: UIButton?
    var post: Post?

    // other stuff...
}

Then you can walk up the view hierarchy from the button to find the PostCell, and get the post from it:

@objc func didTapCommentButton(sender: UIButton) {
    var ancestor = sender.superview
    while ancestor != nil && !(ancestor! is PostCell) {
        ancestor = view.superview
    }
    guard let cell = ancestor as? PostCell,
        post = cell.post
        else { return }

    // Do something with post here
}

Upvotes: 59

Related Questions