swiftuitextfieldselectorbecomefirstresponderinputview

How to set textField becomeFirstResponder via selector


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.


Solution

  • 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.sstatic.net/ugFwK.jpg