Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty Space added at the end when we use KeyboardAvoidingView with ScrollView #35639

Closed
vkundapur opened this issue Dec 14, 2022 · 8 comments
Closed
Labels
API: Keyboard Component: KeyboardAvoidingView Component: ScrollView Needs: Triage 🔍 Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@vkundapur
Copy link

Description

When I use KeyboardAvoidingView inside a ScrollView with behaviour set to padding. Empty space gets added to the end of the screen each time I switch between the InputText.

Version

0.70.6

Output of npx react-native info

info Fetching system and libraries information...
System:
    OS: macOS 13.0.1
    CPU: (10) arm64 Apple M1 Max
    Memory: 3.79 GB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.15.0 - ~/.nvm/versions/node/v16.15.0/bin/node
    Yarn: 1.22.18 - ~/.nvm/versions/node/v16.15.0/bin/yarn
    npm: 8.10.0 - ~/.nvm/versions/node/v16.15.0/bin/npm
    Watchman: Not Found
  Managers:
    CocoaPods: 1.11.3 - /Users/vivek/.rvm/gems/ruby-2.7.5/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1
    Android SDK:
      API Levels: 21, 23, 28, 29, 30, 31, 33
      Build Tools: 30.0.2, 30.0.3, 31.0.0, 32.0.0, 32.1.0, 33.0.0, 33.0.0
      System Images: android-33 | Google APIs ARM 64 v8a
      Android NDK: Not Found
  IDEs:
    Android Studio: Not Found
    Xcode: 14.2/14C18 - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.15 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.1.0 => 18.1.0
    react-native: 0.70.6 => 0.70.6
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Steps to reproduce

I am using the KeyboardAvoidingView within the ScrollView. The idea is to have the Button in the footer.

Empty Space is added at the end of the screen when we use KeyboardAvoidingView with ScrollView. For iOS, I am using behaviour as padding and have set a keyboardVerticalOffset value of 70.

I have attached the sample code with which I am able to regenerate the issue.

Video showing what the issue is:

Screen.Recording.2022-12-14.at.2.55.43.PM.mov

Snack, code example, screenshot, or link to a repository

Attaching the sample code with which I am getting the issue.

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * Generated with the TypeScript template
 * https://github.com/react-native-community/react-native-template-typescript
 *
 * @format
 */

import React from 'react';
import {
  KeyboardAvoidingView,
  Platform,
  SafeAreaView,
  ScrollView,
  StatusBar,
  Text,
  TextInput,
  TouchableOpacity,
  useColorScheme,
  View,
} from 'react-native';

const App = () => {
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? '#F0F0E6' : '#F0F0E6',
  };

  return (
    <SafeAreaView style={[backgroundStyle, {flex: 1}]}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <View style={{flex: 1}}>
        <ScrollView
          contentContainerStyle={{flexGrow: 1}}
          keyboardShouldPersistTaps={'handled'}
          style={backgroundStyle}>
          <KeyboardAvoidingView
            behavior={Platform.OS === 'ios' ? 'padding' : undefined}
            keyboardVerticalOffset={70}
            style={{flex: 1, justifyContent: 'space-between'}}>
            <View style={{flexGrow: 1, marginHorizontal: 10}}>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 1</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 2</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 3</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 4</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 5</Text>
              <Input placeHolder={'Input 1'} />
              <Input placeHolder={'Input 2'} />
              <Input placeHolder={'Input 3'} />
            </View>
            <TouchableOpacity style={{marginVertical: 20}}>
              <View
                style={{
                  backgroundColor: '#3498db',
                  marginHorizontal: 20,
                  alignItems: 'center',
                  borderRadius: 10,
                }}>
                <Text style={{fontSize: 22, marginVertical: 20}}>Button</Text>
              </View>
            </TouchableOpacity>
          </KeyboardAvoidingView>
        </ScrollView>
      </View>
    </SafeAreaView>
  );
};

const Input = ({placeHolder}: {placeHolder: string}) => {
  return (
    <View
      style={{
        borderWidth: 1,
        borderColor: 'rgba(220,220,220,1)',
        borderRadius: 10,
        marginVertical: 10,
        backgroundColor: '#FFFFFF',
      }}>
      <TextInput
        placeholder={placeHolder}
        style={{
          fontSize: 22,
          marginVertical: 20,
          marginHorizontal: 10,
        }}
      />
    </View>
  );
};

export default App;
@hanarotg
Copy link

try not to include TouchableOpacity in KeyboardAvoidingView

<ScrollView
          contentContainerStyle={{flexGrow: 1}}
          keyboardShouldPersistTaps={'handled'}
          style={backgroundStyle}>
          <KeyboardAvoidingView
            behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
            keyboardVerticalOffset={70}
            style={{flex: 1, justifyContent: 'space-between'}}>
            <View style={{flex: 1, marginHorizontal: 10}}>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 1</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 2</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 3</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 4</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 5</Text>
              <Input placeHolder={'Input 1'} />
              <Input placeHolder={'Input 2'} />
              <Input placeHolder={'Input 3'} />
            </View>
          </KeyboardAvoidingView>
          <TouchableOpacity style={{marginVertical: 20}}>
            <View
              style={{
                backgroundColor: '#3498db',
                marginHorizontal: 20,
                alignItems: 'center',
                borderRadius: 10,
              }}>
              <Text style={{fontSize: 22, marginVertical: 20}}>Button</Text>
            </View>
          </TouchableOpacity>
        </ScrollView>

@vkundapur
Copy link
Author

try not to include TouchableOpacity in KeyboardAvoidingView

<ScrollView
          contentContainerStyle={{flexGrow: 1}}
          keyboardShouldPersistTaps={'handled'}
          style={backgroundStyle}>
          <KeyboardAvoidingView
            behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
            keyboardVerticalOffset={70}
            style={{flex: 1, justifyContent: 'space-between'}}>
            <View style={{flex: 1, marginHorizontal: 10}}>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 1</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 2</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 3</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 4</Text>
              <Text style={{fontSize: 22, marginVertical: 20}}>Label 5</Text>
              <Input placeHolder={'Input 1'} />
              <Input placeHolder={'Input 2'} />
              <Input placeHolder={'Input 3'} />
            </View>
          </KeyboardAvoidingView>
          <TouchableOpacity style={{marginVertical: 20}}>
            <View
              style={{
                backgroundColor: '#3498db',
                marginHorizontal: 20,
                alignItems: 'center',
                borderRadius: 10,
              }}>
              <Text style={{fontSize: 22, marginVertical: 20}}>Button</Text>
            </View>
          </TouchableOpacity>
        </ScrollView>

That just moves the Button out of the visible area. The empty space still keeps on adding.

Untitled.mov

@vkundapur
Copy link
Author

I hope the sample code is sufficient to regenerate the issue. Let me know if you need any other info.

@vkundapur
Copy link
Author

Any updates on this or do we have a workaround available?

@hanarotg
Copy link

Any updates on this or do we have a workaround available?

as you replied, a solution I wrote did not fix this issue,

I'm trying to fix this issue by updating Libraries/Components/Keyboard/KeyboardAvoidingView

I'll comment in this issue, if any updates confirmed

@hanarotg
Copy link

hanarotg commented Feb 7, 2023

Solution

1. Open KeyboardAvoidingView.js

Open KeyboardAvoidingView.js placed in /node_modules/react-native/Libraries/Components/Keyboard

2. import Dimensions library

import Diemensions lib in KeyboardAvoidingView.js

import Dimensions from '../../Utilities/Dimensions'

3. find _relativeKeyboardHeight function and edit as follows

 async _relativeKeyboardHeight(
    keyboardFrame: KeyboardMetrics,
  ): Promise<number> {
const frame = this._frame;
    if (!frame || !keyboardFrame) {
      return 0;
    }
    // On iOS when Prefer Cross-Fade Transitions is enabled, the keyboard position
    // & height is reported differently (0 instead of Y position value matching height of frame)
    if (
      Platform.OS === 'ios' &&
      keyboardFrame.screenY === 0 &&
      (await AccessibilityInfo.prefersCrossFadeTransitions())
    ) {
      return 0;
    }

    const keyboardY =
      keyboardFrame.screenY - (this.props.keyboardVerticalOffset ?? 0);
 if (this.props.behavior === 'height') {
      return Math.max(
        this.state.bottom + frame.y + frame.height - keyboardY,
        0,
      );
    }

    const deviceY = Dimensions.get('screen').height // <-- INSERT THIS CODE
    return Math.max(frame.y + deviceY - keyboardY, 0); // <-- EDIT frame.height TO deviceY
}

It works!

Simulator.Screen.Recording.-.iPhone.14.-.2023-02-07.at.19.35.32.mp4

Tip: recommend to use keyboardVerticalOffset as 0

Reason

The problem was frame.height. The frame.height value was calculated and applied in an incomprehensible manner.

As a result, it was maintaining and increasing the paddingBottom even after disabling the keyboard was deactivated. Therefore, I modified the code in a different way without using frame.height.

in case of keyboardVerticalOffset is 0,
KeyboardY returns (full screen height deviceY) - (keyboard height) when keyboard is enabled
and returns (full screen height deviceY) when keyboard is disabled.

Therefore, additional paddingBottom is set to (keyboard height) when the keyboard is activated,
(Math.max(frame.y + deviceY - keyboardY, 0) => deviceY - keyboardY)

and paddingBottom is set to zero when the keyboard is disabled.
(Math.max(frame.y + deviceY - keyboardY, 0) => 0)

If you have any feedback such as knowing the exact calculation method for frame.height, please let me know anytime.

Well.. I hope it helps.
happy coding!

@github-actions
Copy link

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 12, 2023
@github-actions
Copy link

This issue was closed because it has been stalled for 7 days with no activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API: Keyboard Component: KeyboardAvoidingView Component: ScrollView Needs: Triage 🔍 Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants