Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
353 views
in Technique[技术] by (71.8m points)

ios - Swift 3 protocol extension using selector error

I have what I thought to be a very simple protocol extension for my UIViewControllers providing the capability to dismiss a keyboard through a tap gesture. Here's my code:

@objc protocol KeyboardDismissing { 
    func on(tap: UITapGestureRecognizer)
}

extension KeyboardDismissing where Self: UIViewController {

    func addDismissalGesture() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(Self.on(tap:)))
        view.addGestureRecognizer(tap)
    }

    func on(tap: UITapGestureRecognizer) {
        dismissKeyboard()
    }

    func dismissKeyboard() {
        view.endEditing(true)
    }
}

The problem is that the above code throws a compile error on this line:

let tap = UITapGestureRecognizer(target: self, action: #selector(Self.on(tap:)))

With the error message:

Argument of '#selector' refers to instance method 'on(tap:)' that is not exposed to Objective-C

with the suggestion to "fix it" by adding @objc before func on(tap: UITapGestureRecognizer)

Ok fine, I add the tag:

@objc func on(tap: UITapGestureRecognizer) {
    dismissKeyboard()
}

But then, it throws a different compile error on this newly added @objc tag with the error message:

@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

with the suggestion to "fix it" by removing the exact same tag I was just told to add.

I originally thought adding @objc before my protocol definition would solve any #selector problems but apparently that's not the case, and these cyclical error messages/suggestions aren't helping in the slightest. I've gone down a wild goose chase of adding/removing @objc tags everywhere, marking methods as optional, putting methods in the protocol's definition, etc.

It also doesn't matter what I put in the protocol definition Leaving the extension the same, the following example does not work nor does any combination of the declared methods in the protocol's definition:

@objc protocol KeyboardDismissing { 
    func on(tap: UITapGestureRecognizer)
}

This tricks me into thinking it works by compiling as a stand alone protocol, but the second I try to add it to a view controller:

class ViewController: UIViewController, KeyboardDismissing {}

it spits back the original error.

Can someone explain what I'm doing wrong and how I can compile this?

Note:

I've looked at this question but it is for Swift 2.2 not Swift 3 nor does the answer compile as soon as you create a view controller class that inherits from the protocol defined in the example.

I've also looked at this question but the answer uses NotificationCenter which is not what I'm after.

If there are any other seemingly duplicate questions, please let me know.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Matt's answer is correct. However, I would just add that, if you are dealing with #selector to use from a NotificationCenter notification, you could try to avoid #selector by using the closure version.

Example:

Instead of writing:

extension KeyboardHandler where Self: UIViewController {

    func startObservingKeyboardChanges() {

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillShow(_:)),
            // !!!!!            
            // compile error: cannot be included in a Swift protocol
            name: .UIKeyboardWillShow,
            object: nil
        )
    }

     func keyboardWillShow(_ notification: Notification) {
       // do stuff
    }
}

you could write:

extension KeyboardHandler where Self: UIViewController {

    func startObservingKeyboardChanges() {

        // NotificationCenter observers
        NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in
            self?.keyboardWillShow(notification)
        }
    }

    func keyboardWillShow(_ notification: Notification) {
       // do stuff
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...