diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index a520d17d2875fd..06356c68158696 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -187,7 +187,7 @@ const ScrollResponderMixin = { if ( this.props.keyboardShouldPersistTaps === 'handled' && - currentlyFocusedInput != null && + this.scrollResponderKeyboardIsDismissible() && e.target !== currentlyFocusedInput ) { return true; @@ -224,7 +224,6 @@ const ScrollResponderMixin = { // and a new touch starts with a non-textinput target (in which case the // first tap should be sent to the scroll view and dismiss the keyboard, // then the second tap goes to the actual interior view) - const currentlyFocusedTextInput = TextInputState.currentlyFocusedInput(); const {keyboardShouldPersistTaps} = this.props; const keyboardNeverPersistTaps = !keyboardShouldPersistTaps || keyboardShouldPersistTaps === 'never'; @@ -241,7 +240,7 @@ const ScrollResponderMixin = { if ( keyboardNeverPersistTaps && - currentlyFocusedTextInput != null && + this.scrollResponderKeyboardIsDismissible() && e.target != null && !TextInputState.isTextInput(e.target) ) { @@ -251,6 +250,31 @@ const ScrollResponderMixin = { return false; }, + /** + * Do we consider there to be a dismissible soft-keyboard open? + */ + scrollResponderKeyboardIsDismissible: function(): boolean { + const currentlyFocusedInput = TextInputState.currentlyFocusedInput(); + + // We cannot dismiss the keyboard without an input to blur, even if a soft + // keyboard is open (e.g. when keyboard is open due to a native component + // not participating in TextInputState). It's also possible that the + // currently focused input isn't a TextInput (such as by calling ref.focus + // on a non-TextInput). + const hasFocusedTextInput = + currentlyFocusedInput != null && + TextInputState.isTextInput(currentlyFocusedInput); + + // Even if an input is focused, we may not have a keyboard to dismiss. E.g + // when using a physical keyboard. Ensure we have an event for an opened + // keyboard, except on Android where setting windowSoftInputMode to + // adjustNone leads to missing keyboard events. + const softKeyboardMayBeOpen = + this.keyboardWillOpenTo != null || Platform.OS === 'android'; + + return hasFocusedTextInput && softKeyboardMayBeOpen; + }, + /** * Invoke this from an `onResponderReject` event. * @@ -325,7 +349,7 @@ const ScrollResponderMixin = { if ( this.props.keyboardShouldPersistTaps !== true && this.props.keyboardShouldPersistTaps !== 'always' && - currentlyFocusedTextInput != null && + this.scrollResponderKeyboardIsDismissible() && e.target !== currentlyFocusedTextInput && !this.state.observedScrollSinceBecomingResponder && !this.state.becameResponderWhileAnimating