-
Notifications
You must be signed in to change notification settings - Fork 647
iOS height calculation is wrong? #389
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
Comments
This doesn't seem to be happening on android but on ios i can see an extra overflow happening. |
My fix was not to use the library at all, but make a reduced version of
this component that doesn't use any of the RN undocumented APIs to scroll
and stuff. RN will correctly scroll to the input (at least version 0.60).
So all you have to do is adjust some of the component insets. This also has
the advantage of working with nested scroll elements.
Let me know if you need some code samples.
…On Thu, Nov 14, 2019 at 11:30 AM Andrey ***@***.***> wrote:
My case probably related. When Keyboard focus moves to TextInput that
partially overlapped by previously opened keyboard then scrolling is wrong.
Demo:
[image: appVideo]
<https://user-images.githubusercontent.com/1592231/68865438-dfd81c80-0703-11ea-984d-ba764423bd0a.gif>
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#389?email_source=notifications&email_token=ALU263BRVB5GNAJ37J3QUTLQTVOJDA5CNFSM4IY35AZKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEECATDA#issuecomment-553912716>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ALU263FBTBEA7HZHLFNIKK3QTVOJDANCNFSM4IY35AZA>
.
|
@cristianoccazinsp - code samples would be great! |
Alright, here it goes. This is my version of the keyboard aware scrollview without all the internal code usage. The only caveat is that it won't work with multine text inputs unless they have On the other hand, it has the advantage that nested ScrollViews will work just fine and restore their scroll position for both iOS and Android. Note: I've been using this on a few projects, but I can't guarantee it will work as expected. Let me know if you find any issues. KeyboardShiftView.js import React, { Component } from 'react';
import { Keyboard, ScrollView, Platform } from 'react-native';
import {runAfterInteractions} from './utils';
// helper that calls interaction manager run after interactions
// but with a tiny timeout to also give time to other code to run
// some RN change broke runAfterInteractions in a way that it no longer has a huge delay as it used to
// so we add one here
// returns a cancellable object
function runAfterInteractions(fun, to=25){
let prom = null;
let timeout = setTimeout(() => {
prom = InteractionManager.runAfterInteractions(fun);
}, to);
return () => {
clearTimeout(timeout);
if(prom){
prom.cancel();
prom = null;
}
}
}
// Component similar to react-native-keyboard-aware-scroll-view but with nested scrolling support and no internal APIs usage
// Input fields will be auto scrolled automatically. For multi line, scrollEnabled={false} must be used.
// props:
// Component: FlatList | ScrollView
// extraHeight
// innerRef
const IS_IOS = Platform.OS == 'ios';
const showEvent = IS_IOS ? 'keyboardWillShow' : 'keyboardDidShow';
const hideEvent = IS_IOS ? 'keyboardWillHide' : 'keyboardDidHide';
// using native props to avoid re-renders
class KeyboardShift extends Component {
constructor(props){
super(props);
this.state = {
}
this.scroll = null;
this.lastScroll = null;
}
componentDidMount() {
this.keyboardShowSub = Keyboard.addListener(showEvent, this.handleKeyboardShow);
this.keyboardHideSub = Keyboard.addListener(hideEvent, this.handleKeyboardHide);
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
this.lastScroll = null;
this.keyboardShowSub.remove();
this.keyboardHideSub.remove();
if(this.cancelHide){
this.cancelHide();
this.cancelHide = null;
}
if(this.cancelShow){
this.cancelShow();
this.cancelShow = null;
}
}
onLayout = (event) => {
this.layout = event.nativeEvent.layout
}
onScroll = (event) => {
if(event.nativeEvent.contentOffset){
this.lastScroll = event.nativeEvent.contentOffset.y;
}
this.props.onScroll && this.props.onScroll(event);
}
onScrollEndDrag = (event) =>{
// if user manually scrolled, do not restore scroll
this.scroll = null;
this.props.onScrollEndDrag && this.props.onScrollEndDrag(event);
}
setRef = (r) => {
this.ref = r;
if(this.props.innerRef){
this.props.innerRef(r);
}
}
render() {
const { Component, innerRef, onScroll, onScrollEndDrag, keyboardDismissMode, ...rest } = this.props;
return (
<Component
ref={this.setRef}
onLayout={this.onLayout}
keyboardDismissMode={keyboardDismissMode}
automaticallyAdjustContentInsets={false}
scrollEventThrottle={16}
onScroll={this.onScroll}
onScrollEndDrag={this.onScrollEndDrag}
{...rest} />
);
}
scrollTo = (scroll) => {
if(this.ref.scrollToOffset){
this.ref.scrollToOffset({
offset: scroll,
animated: true
})
}
else{
this.ref.scrollTo({
animated: true,
y: scroll
})
}
}
handleKeyboardShow = (event) => {
if(this.cancelHide){
this.cancelHide();
this.cancelHide = null;
}
if(this.cancelShow){
this.cancelShow();
}
// need to give time to other stuff to hide if any
// also set last scroll to 0 if it wasn't defined since we always must scroll on restore
this.scroll = this.lastScroll || (this.lastScroll = 0);
this.cancelShow = runAfterInteractions(()=>{
if(this.layout && this.ref && this.mounted){
const keyboardHeight = event.endCoordinates.height;
//const keyboardPosition = event.endCoordinates.screenY;
let gap = keyboardHeight + this.props.extraHeight;
// inset is also added on timeout so it doesn't look too awkward
this.ref.setNativeProps({contentInset: { bottom: gap }});
this.cancelShow = null;
}
}, 250);
}
handleKeyboardHide = () => {
if(this.cancelHide){
this.cancelHide();
this.cancelHide = null;
}
if(this.cancelShow){
this.cancelShow();
this.cancelShow = null;
}
// only fire this if we actually did something
if(this.lastScroll != null && this.ref){
// update inset right away to remove visible area as soon as possible
this.ref.setNativeProps({contentInset: { bottom: 0 }});
this.cancelHide = runAfterInteractions(()=>{
if(this.ref && this.mounted){
let scroll = this.scroll != null ? this.scroll : this.lastScroll;
this.scroll = null;
if(scroll != null && this.props.enableResetScrollToCoords){
this.scrollTo(scroll + 0.001);
}
this.cancelHide = null;
}
}, 150);
}
}
}
// very similar component with a few differences
// made to work with android's android:windowSoftInputMode="adjustResize"
// other modes *might* might work, but test it.
class KeyboardShiftAndroid extends Component {
constructor(props){
super(props);
this.state = {
}
this.scroll = null;
this.lastScroll = null;
}
componentDidMount() {
// Android events are far more limited
this.keyboardShowSub = Keyboard.addListener(showEvent, this.handleKeyboardShow);
this.keyboardHideSub = Keyboard.addListener(hideEvent, this.handleKeyboardHide);
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
this.lastScroll = null;
this.keyboardShowSub.remove();
this.keyboardHideSub.remove();
if(this.cancelHide){
this.cancelHide();
this.cancelHide = null;
}
if(this.cancelShow){
this.cancelShow();
this.cancelShow = null;
}
}
onLayout = (event) => {
this.layout = event.nativeEvent.layout
}
onScroll = (event) => {
if(event.nativeEvent.contentOffset){
// update this with a timeout since scrolling might happen
// before keyboard show event
let scroll = event.nativeEvent.contentOffset.y;
if(this.scrollTimeout){
clearTimeout(this.scrollTimeout);
}
this.scrollTimeout = setTimeout(()=>{
this.lastScroll = scroll;
}, 260); // just a little higher than keyboard show time
}
this.props.onScroll && this.props.onScroll(event);
}
onScrollEndDrag = (event) =>{
// if user manually scrolled, do not restore scroll
this.scroll = null;
this.props.onScrollEndDrag && this.props.onScrollEndDrag(event);
}
setRef = (r) => {
this.ref = r;
if(this.props.innerRef){
this.props.innerRef(r);
}
}
render() {
const { Component, innerRef, onScroll, onScrollEndDrag, ...rest } = this.props;
return (
<Component
ref={this.setRef}
onLayout={this.onLayout}
automaticallyAdjustContentInsets={false}
scrollEventThrottle={16}
onScroll={this.onScroll}
onScrollEndDrag={this.onScrollEndDrag}
{...rest} />
);
}
scrollTo = (scroll) => {
if(this.ref.scrollToOffset){
this.ref.scrollToOffset({
offset: scroll,
animated: true
})
}
else{
this.ref.scrollTo({
animated: true,
y: scroll
})
}
}
// this relies on the fact that keyboard did show
// happens before everything scrolls up due to height changes
handleKeyboardShow = (event) => {
if(this.cancelHide){
this.cancelHide();
this.cancelHide = null;
}
// need to give time to other stuff to hide if any
// also set last scroll to 0 if it wasn't defined since we always must scroll on restore
this.scroll = this.lastScroll || (this.lastScroll = 0);
}
handleKeyboardHide = () => {
if(this.cancelHide){
this.cancelHide();
this.cancelHide = null;
}
// only fire this if we actually did something
if(this.lastScroll != null && this.ref){
this.cancelHide = runAfterInteractions(()=>{
if(this.ref && this.mounted && this.scroll != null && this.props.enableResetScrollToCoords){
this.scrollTo(this.scroll);
this.cancelHide = null;
}
}, 150);
}
}
}
export default class KeyboardShiftView extends React.Component{
render(){
let {Component, extraHeight, innerRef, androidEnabled, ...rest} = this.props;
return IS_IOS ?
<KeyboardShift Component={Component} extraHeight={extraHeight} innerRef={innerRef} {...rest}/>
:
(androidEnabled ? <KeyboardShiftAndroid Component={Component} extraHeight={extraHeight} innerRef={innerRef} {...rest}/> : <Component ref={innerRef} {...rest}/>)
}
}
KeyboardShiftView.defaultProps = {
Component: ScrollView,
extraHeight: 10,
keyboardDismissMode: 'interactive',
enableResetScrollToCoords: true,
keyboardShouldPersistTaps: 'handled',
androidEnabled: true
} |
I've added insetOnly option. It disables autoScroll and only adds keyboard padding. That is all the app needs if only onscreen text inputs can be focused. PR: #403 |
I had same problem. Is there any way to fix this issue please? |
A lot of problems with this lib... My solution was just to use the |
Still facing the same issue, Any update on this thread? |
Can you guys review this? https://github.com/APSL/react-native-keyboard-aware-scroll-view/blob/master/lib/KeyboardAwareHOC.js#L391
First, you are using a default extra height of 75 (probably to account for some height of an input). This is already a bit odd.
However, on the above code you are using the input's bottom location to decide if the screen should scroll or not. This seems fine, but you are also adding the extra height (which is also fine). What is wrong, however, is that you are not using the input's bottom location to actually scroll. You should be also adding the input height to the extra height calculation at the scroll code here (https://github.com/APSL/react-native-keyboard-aware-scroll-view/blob/master/lib/KeyboardAwareHOC.js#L463). If you did this, you wouldn't need a 75 default for extra height and a 0 would scroll just fine to the bottom of the element.
With the above, the final result is that you are adding some extra height to correctly scroll to the bottom of the input, but this also causes the checks to fire earlier (because you are using the bottom of the input + extra height to decide if scrolling or not). So you are basically checking to scroll for 1 value (input bottom + extra height), but only scrolling to input top + extra height. This makes scrolling very odd.
I can't tell if this is an issue also for Android since I'm not using it in this case.
The text was updated successfully, but these errors were encountered: