How to set textField becomeFirstResponder via selector

175 Views Asked by At

I wrote a UITextfield extension to build my own toolbar for custom inputs:

func addToolbar(selector: Selector? = nil) {
    let action = selector == nil ? #selector(resignFirstResponder) : selector
    
    let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: action)

    let toolbar = UIToolbar()
    toolbar.items = [flexibleSpace, doneButton]
    toolbar.sizeToFit()
    toolbar.layoutIfNeeded()
    
    inputAccessoryView = toolbar
    returnKeyType = .done
}

I use it like this:

let myPickerView = UIPickerView()
myPickerView.dataSource = myPickerViewBehavior
myPickerView.delegate = myPickerViewBehavior
    
if let selected = value {
    let selectedIndex = PickableItemType.index(of: selected.rawValue)
    myPickerView.selectRow(selectedIndex, inComponent: 0, animated: true)
}
    
myTextField.delegate = self
myTextField.inputView = myPickerView
myTextField.addToolbar(selector: #selector(nextTextField.becomeFirstResponder))

As you can imagine I want the next UITextField to becomeFirstResponder after the user pressed done in the toolbar. But nothing happening. For testing I've tried using a simple @objs func with print statement and/or nextTextField.becomeFirstResponder inside, same result. I've also tried to use UITapGestureRecognizer with #selector(nextTextField.becomeFirstResponder), also same result.

So I guess it has something to do with the #selector mechanic why the code doesn't work.

P.S.: if selector == nil inside addToolbar , #selector(resignFirstResponder) works fine.

1

There are 1 best solutions below

1
On BEST ANSWER

I believe the problem is caused by the wrong target: while setting up doneButton.

If you also supply the target in addToolbar: method like:

func addToolbar(target: Any? = nil, selector: Selector? = nil) {
    let object = target ?? self
    let action = selector ?? #selector(resignFirstResponder)
    
    let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: object, action: action)

    let toolbar = UIToolbar()
    toolbar.items = [flexibleSpace, doneButton]
    toolbar.sizeToFit()
    toolbar.layoutIfNeeded()
    
    inputAccessoryView = toolbar
    returnKeyType = .done
}

This should work:

firstTextField.addToolbar(target: secondTextField, selector: #selector(becomeFirstResponder))

Here's a small test result:

https://i.stack.imgur.com/ugFwK.jpg