React-native: [ScrollResponder][Feature] Scroll to native handle only if handle is under the keyboard

Created on 31 May 2016  路  7Comments  路  Source: facebook/react-native

As of now, _ScrollResponder_ implements method to scroll native handle to the keyboard. Handle, usually, is a text input but it could be whatever component...

Current solution:

/**
   * This method should be used as the callback to onFocus in a TextInputs'
   * parent view. Note that any module using this mixin needs to return
   * the parent view's ref in getScrollViewRef() in order to use this method.
   * @param {any} nodeHandle The TextInput node handle
   * @param {number} additionalOffset The scroll view's top "contentInset".
   *        Default is 0.
   * @param {bool} preventNegativeScrolling Whether to allow pulling the content
   *        down to make it meet the keyboard's top. Default is false.
   */
  scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle: any, additionalOffset?: number, preventNegativeScrollOffset?: bool) {
    this.additionalScrollOffset = additionalOffset || 0;
    this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
    UIManager.measureLayout(
      nodeHandle,
      ReactNative.findNodeHandle(this.getInnerViewNode()),
      this.scrollResponderTextInputFocusError,
      this.scrollResponderInputMeasureAndScrollToKeyboard
    );
  },

Issue with current solution:
Handle scrolls to bottom of the keyboard even if it's not covered by the keyboard itself. This looks very bad and causes unnecessary distraction.

Proposed solution:
What I propose is to implement very similar method which would measure handle's dimensions and it's Y position relative to viewport/screen (not "document", which might and ofter is higher than viewport/screen height) and scroll only if handle is under the keyboard. In order to to so, _ScrollResponder_ has to retain scroll offset (content offset) in one of it's method.

// ScrollResponder.js
scrollResponderHandleScroll: function(e: Event) {
  this.state.observedScrollSinceBecomingResponder = true;
  this.props.onScroll && this.props.onScroll(e);
  // retain contentOffset
  this.contentOffset = e.nativeEvent.contentOffset;
},

// Expose method which solves the issue
scrollResponderScrollNativeHandleToKeyboardIfCovered: function(nodeHandle: any, additionalOffset?: number, preventNegativeScrollOffset?: bool) {
  this.additionalScrollOffset = additionalOffset || 0;
  this.preventNegativeScrollOffset = !!preventNegativeScrollOffset;
  UIManager.measureLayout(
    nodeHandle,
    ReactNative.findNodeHandle(this.getInnerViewNode()),
    this.scrollResponderTextInputFocusError,
    this.scrollResponderInputMeasureAndScrollToKeyboardIfCovered
  );
},

// Measure handle's position and decide whether to scroll or not to.
// Under the hood, original `scrollResponderInputMeasureAndScrollToKeyboard` method is used.
scrollscrollResponderInputMeasureAndScrollToKeyboardIfCovered(left: number, top: number, width: number, height: number) {
  var bottommostCoord = top + height - this.contentOffset.y;
  var keyboardScreenY = Dimensions.get('window').height;
  if (this.keyboardWillOpenTo) {
    keyboardScreenY = this.keyboardWillOpenTo.endCoordinates.screenY;
  }

  if (bottommostCoord - keyboardScreenY > 0) {
    // Use existing method to do measurements and scroll to handle to keyboard.
    this.scrollResponderInputMeasureAndScrollToKeyboard(left, top, width, height);
  }
},

Questions:
What I am not sure about is scrollResponderScrollNativeHandleToKeyboardIfCovered which is mostly copy-paste of scrollResponderScrollNativeHandleToKeyboard. The only difference is it's "on success" callback for UIManager.measureLayout call.

  • Should standalone scrollscrollResponderInputMeasureAndScrollToKeyboardIfCovered be implemented or in order to avoid almost 1:1 copy should scrollscrollResponderInputMeasureAndScrollToKeyboard accept one more argument?
  • If scrollscrollResponderInputMeasureAndScrollToKeyboard accepts one more argument wouldn't it be too many arguments (it's already too many to my taste)?
  • Is it possible for scrollscrollResponderInputMeasureAndScrollToKeyboard to break current API and have something like
    scrollscrollResponderInputMeasureAndScrollToKeyboard(layout: Object, dontScrollIfNotCovered: boolean);
    Or is it something you'd like to avoid?
  • do you have any better method naming in mind?

If we clear those questions and you are interested, I'd be happy to send PR.

Locked

Most helpful comment

Is there a way to get KeyboardAvoidingView to work with a TextInput that's inside a ScrollView?

All 7 comments

Can you confirm if this is still an issue for you? I'm not able to replicate this on current master.

@chirag04 could you point me to recent updates of what changed, related to this issue? I've seen there is KeyboardAvoidingView available now, which could be used to shrink inner ScrollView. I am about to try that out...

Is there a way to get KeyboardAvoidingView to work with a TextInput that's inside a ScrollView?

@chirag04 It didn't work for me.

Closing this issue because it has been inactive for a while. If you think it should still be opened let us know why.

Was this page helpful?
0 / 5 - 0 ratings