0

I have a text entry form implemented as a UITableViewController where each field in the form is a subview of a cell in the tableview. On my keyboard I have "next" and "prev" buttons that do the expected and call becomeFirstResponder() on the text field one cell before or one cell after the current first responder.

This call always succeeds when "next" is pressed: the call to the next field's becomeFirstResponder() returns true, even if that cell is currently below the bottom of the screen. However, when "prev" is pressed, the call to the previous field's becomeFirstResponder() returns true if that field's parent view is currently visible on the screen but returns false when it is above the top of the screen.

Some things I've considered and eliminated as the cause:

1) The table view uses static cells and therefore the parent cell of the targeted field is not recycled for reuse. It is part of the view hierarchy at all times and the controller keeps a reference to it.

2) The canBecomeFirstResponder property of the field I'm asking to become first responder returns true, even when not visible.

3) The current first responder is not blocking it by refusing to give up first responder status. first responder == nil at the time becomeFirstResponder() is called on the relevant field.

4) The delegate of the field I'm asking to become first responder returns "true" when its shouldBeginEditing(_) field is invoked for the relevant text field.

My question is, given (1) - (4) above, what could be causing becomeFirstResponder() to return 'false'? I've tried but have been unsuccessful in finding more detail about UIResponder's implementation of that method.

Here is the handler for the prev press and what the state looks like at that time. (The while loop is just for debugging of course.)

func keyboardControls(_ keyboardControls: BSKeyboardControls, selectedField field: UIView, inDirection direction: BSKeyboardControlsDirection) {

    var loopCount = 1
    let mainWindow = AppDelegate.appDelegate().window!

    while field.isFirstResponder == false {
        print(loopCount) // will print from 1 to overflow
        print(field.canBecomeFirstResponder) // prints true
        print(field.window == mainWindow) // prints true
        print(tableView.subviews.contains(field.superview!.superview!)) // prints true
        print(mainWindow.currentFirstResponder()) // prints currently active field on first pass, nil thereafter
        field.becomeFirstResponder() // returns false
        loopCount += 1
    }
}
iKe
  • 241
  • 2
  • 4
  • Please share some code if you can – Gihan Feb 16 '18 at 21:52
  • @Gihan Is there any code you're interested in in particular? There are a lot of components here and if I post all of the potentially relevant code it will likely muddy the waters rather than clarify. What I'm really after is a deeper understanding of becomeFirstResponder() and what conditions might make it return 'false.' – iKe Feb 16 '18 at 22:27
  • can you check what mainWindow.currentFirstResponder().canResignFirstResponder() returns ? – Gihan Feb 20 '18 at 17:00

0 Answers0