wix / react-native-navigation

A complete native navigation solution for React Native
https://wix.github.io/react-native-navigation/
MIT License
13.04k stars 2.67k forks source link

[IOS] Keyboard flickering while pushing screen with autofocus TextInput #2622

Closed ACOSW closed 6 years ago

ACOSW commented 6 years ago

Issue Description

Keyboard flicks while pushing screen with autofocus TextInput. Flickering appears only with default ios animation (horizontal-slide), with "fade" animation keyboard appears fine.

Steps to Reproduce / Code Snippets / Screenshots

Simply push to the navigation stack new screen with autofocus input and you'll see same keyboard flickering as on attached gif image:


Environment

pqkluan commented 6 years ago

This problem happens because the second screen is mounted before the push-animation finished. All native events (include keyboards) are share between Screens.

This issue could be solved by using fade animation or delay the TextInput from rendering too soon.

untoldone commented 6 years ago

@pqkluan is there a way to have a text input autofocused and the keyboard skip animation on a new screen without a fade in or flickering?

For example, many apps have this such as Facebook messenger and others (when you select a chat, input is focused and keyboard is up on new screen before transition.

DVSoftware commented 6 years ago

We also have this issue, it's pretty annoying that we have to delay input focusing

Jose4gg commented 5 years ago

This needs to be fixed

pribeh commented 5 years ago

Same. Adding a timeout seems to be the only fix for now.

LRNZ09 commented 5 years ago

I'm also having this issue.

exentrich commented 5 years ago

No better solution besides timeout? Will be awesome if RNN itself fix this problem, because everyone could face this glitch. Also in modals use of autoFocus makes view empty for duration of appear animation. This adds annoying delays before screen body actually showed.

chrise86 commented 5 years ago

The way I'm getting around it is to remove the autoFocus from the input and instead add a ref to the input, setup an event listener and focus when the screen appears:

class MyComponent extends Component {
  componentDidMount() {
    this.navigationEventListener = Navigation.events().bindComponent(this)
  }

  componentWillUnmount() {
    // Not mandatory
    if (this.navigationEventListener) {
      this.navigationEventListener.remove()
    }
  }

  componentDidAppear() {
    if (this.input) this.input.focus()
  }

  render() {
    return (
      <Input ref={ref => this.input = ref} />
    )
  }
}
ATShiTou commented 5 years ago

componentDidMount() { this.navigationEventListener = Navigation.events().bindComponent(this) }

componentWillUnmount() { // Not mandatory if (this.navigationEventListener) { this.navigationEventListener.remove() } }

componentDidAppear() { if (this.input) this.input.focus() }

It's not working for me.

chrise86 commented 5 years ago

@ATShiTou how are you creating your ref?

ATShiTou commented 5 years ago

I had resolved my problem. Mine is showOverlay problem, not push. Thank you anyway.

pribeh commented 5 years ago

@chrise86 's solution is the best for now but this is still happening for us.

BDomantas commented 5 years ago

Why is this closed? It's still an issue in 3.2.0

leonid-shevtsov commented 5 years ago

I have slightly different behavior in my app: when I push a screen with an autofocused input, the screen animates without the keyboard, then jumps back 1/4 of the screen, but now with the keyboard visible, and completes the push animation again. So it kind of jerks back and forth.

exentrich commented 5 years ago

Try to set animations: {push:{waitForRender: true}} in static options=... for screen where you have autofocus input

soroush-b commented 5 years ago

I fixed that by

  componentDidMount() {
    this.navigationEventListener = Navigation.events().bindComponent(this);
  }

  componentWillUnmount() {
    // Not mandatory
    if (this.navigationEventListener) {
      this.navigationEventListener.remove();
    }
  }

  componentDidAppear() {
    this.input.focus();
  }

  render(){
     return <input ref={node => (this.input = node)} />
  }
chrise86 commented 5 years ago

@soroush-b that’s the solution I posted above. For those saying it’s still an issue, I’m not sure there is anything that can be done to “fix it” other than solutions like this. As @pqkluan said early on, it’s due to the component mounting before the native animation is complete.

JohnSmith0602 commented 4 years ago

Try to set animations: {push:{waitForRender: true}} in static options=... for screen where you have autofocus input

Your solution works in RNN v2.27.9, thanks!

L-Yeiser commented 4 years ago

waitForRender removed the animation entirely. As in with that configuration autofocus just stopped working. Had to use the setTimeout approach.

pontusab commented 4 years ago

Still an issue in the latest versions.

nathantqn commented 4 years ago

The current approach that I use:

If your app is using a custom InputField:

import {useNavigationComponentDidAppear} from 'react-native-navigation-hooks';

const CustomInputField = ({shouldAutoFocus}) => {
       const inputRef = useRef(null);

       useNavigationComponentDidAppear(() => {
          if (shouldAutoFocus) {
             inputRef.current && inputRef.current.focus();
          }
       });

       return (
        <TextInput
          ref={inputRef}
          {...restProps}
        />
       )

}

And wherever you use CustomInputField: <CustomInputField shouldAutoFocus />

Peeeep commented 3 years ago

Old thread, I’m aware, but I thought leaving this here might help people who use functional components:

import {useNavigationComponentDidAppear} from 'react-native-navigation-hooks';

…

/**
 * Focus input once screen has appeared for the first time
 */
const inputRef = useRef(null);
const shouldAutoFocus = useRef(true);
useNavigationComponentDidAppear(() => {
  if (shouldAutoFocus.current) {
    inputRef.current?.focus();
    shouldAutoFocus.current = false;
  }
}, componentId);

…

<TextInput
  ref={inputRef}
  …
/>
alienbalcorp commented 7 months ago

You can use it. It's work for me!

Be sure add these imports: import React, {useState, useRef, useEffect} from 'react';

Add useState const [searchValue, setSearchValue] = useState("");

Add useRef const inputSearch = useRef<TextInput>(null);

Add useEffect useEffect(() => { setTimeout(() => { inputSearch.current && inputSearch.current.focus(); }, 450); }, [])

(450 milliseconds because... The default animation duration for screen transitions in React Native Navigation libraries like React Navigation is typically around 300 milliseconds.)

For input

<TextInput onChangeText={(e) => setSearchValue(e)} value={searchValue} placeholder='Search' ref={inputSearch} />

Now animations will be on time and the keyboard will appear without twitching.

Or do it for <Stack.Navigator screenOptions={{ animation: 'none' }}>

Or <Stack.Screen name="Search" component={SearchScreen} options={{ headerShown: false, gestureDirection: "vertical" }} />

without useEffect and without useRef

And add this:

autoFocus={true}

ghostgarrix commented 3 months ago

Still an issue with latest versions if we want to avoid using a timeout