0
votes

I'm trying to make a convenient Binding keyboard to a uiview function. I can't get across this error

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Twitter.LoginVC handleKeyboard:]: unrecognized selector sent to instance 0x7ffbf142e970'

class KeyboardService {

var constraint: NSLayoutConstraint!
var vc: UIViewController!

func bind(bottomConstraint: NSLayoutConstraint, vc: UIViewController) {
    constraint = bottomConstraint
    self.vc = vc
    NotificationService.instance.addKeyboardObservers(onVC: vc, handleKeyboardSelector: #selector(self.handleKeyboard(_:))) // **CRASHES HERE**
}

@objc func handleKeyboard(_ notification: NSNotification) {
    NotificationService.instance.handleKeyboard(notification: notification, bottomConstraint: constraint, vc: vc)
} 
}

Here's my notificationService:

class NotificationService {
static let instance  = NotificationService()

func addKeyboardObservers(onVC vc: UIViewController, handleKeyboardSelector: Selector) {
    NotificationCenter.default.addObserver(vc, selector: handleKeyboardSelector, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(vc, selector: handleKeyboardSelector, name: UIResponder.keyboardWillHideNotification, object: nil)
}
}

EDIT:

class KeyboardService {

var constraint: NSLayoutConstraint!
var vc: UIViewController!

func bind(bottomConstraint: NSLayoutConstraint, vc: UIViewController) {
    constraint = bottomConstraint
    self.vc = vc
    NotificationService.instance.addKeyboardObservers(self, handleKeyboardSelector: #selector(self.handleKeyboard(_:)))
}

@objc func handleKeyboard(_ notification: NSNotification) {
    NotificationService.instance.handleKeyboard(notification: notification, bottomConstraint: constraint, vc: vc)
}

}

EDIT 2:

class KeyboardService {

var constraint: NSLayoutConstraint!
var vc: UIViewController!

func bind(bottomConstraint: NSLayoutConstraint, vc: UIViewController) {
    constraint = bottomConstraint
    self.vc = vc
    NotificationService.instance.addKeyboardObservers(self, handleKeyboardSelector: #selector(handleKeyboard(_:)))
}

@objc func handleKeyboard(_ notification: NSNotification) {
    NotificationService.instance.handleKeyboard(notification: notification, bottomConstraint: constraint, vc: vc)
}
}

In viewDidLoad() of a vc:

KeyboardService().bind(bottomConstraint: loginBtnBackViewBottomConstraint, vc: self)
1

1 Answers

0
votes

You are trying to sent the selector to vc; but it's a UIViewController that doesn't actually have a method called handleKeyboard(_:). You should change to this your registering method:

func addKeyboardObservers(_ observer: Any, handleKeyboardSelector: Selector) {
    NotificationCenter.default.addObserver(observer, selector: handleKeyboardSelector, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(observer, selector: handleKeyboardSelector, name: UIResponder.keyboardWillHideNotification, object: nil)
}

And then when you use it, you'd do:

NotificationService.instance.addKeyboardObservers(self, handleKeyboardSelector: #selector(self.handleKeyboard(_:)))

As you can see you're now telling to trigger the selector on self, because it's actually self that has that method. As a general rule: a selector is sent to an instance, so it's that instance that must have the chosen method.


If you really want to send the selector to a viewController instance you could create an extension that adds the method to every UIViewController

extension UIViewController {

    @objc func handleKeyboard(_ notification: NSNotification) {
        // do your stuff here
    }
}

Then, on registration do:

NotificationService.instance.addKeyboardObservers(onVC: vc, handleKeyboardSelector: #selector(vc.handleKeyboard(_:))) // notice the vc.handleKeyboard instead of self.handleKeyboard

EDIT Try to retain the keyboard service in the view controller:

let keyboardService = KeyboardService()

override func viewDidLoad() {
    super.viewDidLoad()
    keyboardService.bind(bottomConstraint: loginBtnBackViewBottomConstraint, vc: self)
}