iOS 键盘自动填充在关注下一个 TextInput 时会导致内容跳转/故障

问题描述 投票:0回答:1

描述

我的应用程序中有一个页面,其中两个 TextInput 内部视图覆盖整个屏幕。

两个 TextInput 有一个 ref

ref={refTextInput1}
ref={refTextInput2}
。 他们还有一个 onSubmitEditing
onSubmitEditing={() => refTextInput2()}
onSubmitEditing={() => refTextInput1()}

因此,当我按键盘上的“返回”时,焦点将切换到所需的输入,但它也会使整个屏幕跳转/故障,这真的很烦人,也是不想要的。

焦点应该切换到所需的输入,而不会使整个屏幕跳跃/故障。

我在 StackOverflow 和 Github Issues 上到处搜索,但没有发现很多与这个特定问题相关的问题。 起初,我以为我遇到了类似于 #30207 的问题,但经过一段时间尝试查找问题后,我想我找到了它。

问题仅在“设置”>“密码”>“自动填充密码”中启用“自动填充密码”时才会出现(我认为)。 如果我禁用自动填充密码,则不会发生跳转/故障。

我还注意到,在启用自动填充密码的情况下,键盘事件总是在焦点上触发两次,而在禁用自动填充密码的情况下,仅在焦点上触发一次。也可能有关系。 (运行小吃示例时请参阅控制台。)

我查看了其他应用程序,它们在同一类型的登录屏幕中看起来都很好。例如 Instagram 就没有这个问题。

这也可能是我制作屏幕的方式,如果有更好的方法来制作相同或相似的屏幕,我会虚心接受建议。

React Native 版本:

System:
    OS: macOS 11.4
    CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
    Memory: 137.61 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.17.0 - /usr/local/bin/node
    Yarn: Not Found
    npm: 6.14.13 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.10.1 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4
    Android SDK:
      API Levels: 22, 23, 24, 25, 26, 27, 28, 29, 30
      Build Tools: 28.0.3, 29.0.2, 30.0.2
      System Images: android-30 | Google APIs Intel x86 Atom
      Android NDK: Not Found
  IDEs:
    Android Studio: 4.1 AI-201.8743.12.41.6953283
    Xcode: 12.5/12E262 - /usr/bin/xcodebuild
  Languages:
    Java: 1.8.0_271 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 17.0.1 => 17.0.1 
    react-native: 0.64.1 => 0.64.1 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

重现步骤

  1. 确保在“设置”>“密码”>“自动填充密码”中启用“自动填充密码”。
  2. 至少有两个 TextInput,其中一个视图占据整个屏幕空间。
  3. 使用键盘上的“返回”键将焦点放在下一个 TextInput 上。

我还注意到,在启用自动填充密码的情况下,键盘事件总是在焦点上触发两次,而在禁用自动填充密码的情况下,仅在焦点上触发一次。也可能有关系。 (运行小吃示例时请参阅控制台。)

预期结果

按键盘上的“返回”时,焦点应切换到所需的输入,而不会导致整个屏幕跳跃/毛刺。就像在“设置”>“密码”>“自动填充密码”中禁用自动填充密码时一样。

小吃、代码示例、屏幕截图或存储库链接:

Snack 1 是我的问题的简单版本。 小吃 1:简单键盘自动填充问题

Snack 2 更接近我应用程序中的真实内容。 小吃 2:my-app-keyboard-autofill-issue

最小代码示例:(来自 simple-keyboard-autofill-issue

import React, { useState, useRef, useEffect } from 'react';
import { Text, View, KeyboardAvoidingView, TouchableWithoutFeedback, Keyboard, Platform, TextInput } from 'react-native';

export default function App() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const refUsernameInput = useRef(null);
  const refPasswordInput = useRef(null);

  useEffect(() => {
    Keyboard.addListener('keyboardWillShow', keyboardWillShow);
    Keyboard.addListener('keyboardWillHide', keyboardWillHide);
s
    // cleanup function
    return () => {
      Keyboard.removeListener('keyboardWillShow', keyboardWillShow);
      Keyboard.removeListener('keyboardWillHide', keyboardWillHide);
    };
  }, []);

  const keyboardWillShow = () => {
    console.log('keyboardWillShow');
  };

  const keyboardWillHide = () => {
    console.log('keyboardWillHide');
  };

  return (
    <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={{ flex: 1 }}>
      <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
        <View style={{ flex: 1, justifyContent: 'space-around', paddingHorizontal: 20 }}>
            <TextInput
              blurOnSubmit={false}
              keyboardType="email-address"
              placeholder="Username or email"
              textContentType="username" // iOS
              onSubmitEditing={() => refPasswordInput.current.focus()}
              onChangeText={setUsername}
              value={username}
              ref={refUsernameInput}
              style={{
                borderColor: '#000',
                borderWidth: 0.5,
                height: 45,
                paddingHorizontal: 20,
                marginBottom: 5,
              }}
            />
            <TextInput
              blurOnSubmit={false}
              keyboardType="default"
              placeholder="Password"
              secureTextEntry
              textContentType="password" // iOS
              onSubmitEditing={() => refUsernameInput.current.focus()}
              onChangeText={setPassword}
              value={password}
              ref={refPasswordInput}
              style={{
                borderColor: '#000',
                borderWidth: 0.5,
                height: 45,
                paddingHorizontal: 20,
              }}
            />
        </View>
      </TouchableWithoutFeedback>
    </KeyboardAvoidingView>
  );
}

这里可以看到使用 iOS 14.6 的 iPhone 11 Pro 进行的屏幕录制:(注意,屏幕录制时 secureTextEntry 被暂时禁用,问题仍然存在)

https://user-images.githubusercontent.com/64789082/121977389-e7363080-cd53-11eb-9a4c-e26fc364bd03.MP4

ios react-native keyboard react-native-ios autofill
1个回答
0
投票

我也遇到了同样的问题,但是Facebook尚未为此组件提供官方解决方案。我正在思考是什么导致了这个错误,以找出一些有效的解决方案。导致该错误的原因是,使用自动填充时,每次输入值更改时填充值都会更改。为了解决这个问题,我实现了一个 CustomKeyboardAvoidingView 组件,如果平台是 android,该组件将返回官方的 KeyboardAvoidingView 组件;对于 ios,它会返回一个简单的 View,其中将动态设置填充。

const CustomKeyboardAvoidingView = memo(function ({
  children,
  ...rest
}: KeyboardAvoidingViewProps) {
  const platform = Platform.OS;

  const paddingBottom = useSharedValue(0);

  const style = useAnimatedStyle(() => {
    return {
      paddingBottom: paddingBottom.value,
    };
  });
  const animation = useCallback((value: number) => {
    'worklet';
    if (paddingBottom.value < value || !value) {
      paddingBottom.value = withSpring(value, {damping: 50});
    }
  }, []);

  useEffect(() => {
    const setKeyboardIOS = (height: number) => {
      animation(height);
    };

    const willShow = Keyboard.addListener('keyboardWillShow', e =>
      setKeyboardIOS(e.endCoordinates.height),
    );
    const willHide = Keyboard.addListener('keyboardWillHide', () =>
      setKeyboardIOS(0),
    );

    return () => {
      willShow.remove();
      willHide.remove();
    };
  }, []);

  if (platform === 'android') {
    return (
      <KeyboardAvoidingView behavior="height" style={{flex: 1}} {...rest}>
        {children}
      </KeyboardAvoidingView>
    );
  }

  if (platform === 'ios') {
    return (
      <Animated.View
        style={[
          style,
          {
            flex: 1,
          },
        ]}
        {...rest}>
        {children}
      </Animated.View>
    );
  }

  return <View>{children}</View>;
});

CustomKeyboardAvoidingView.displayName = 'CustomKeyboardAvoidingView';

export {CustomKeyboardAvoidingView};

我还添加了一个动画,但它是可选的,您可以使用状态来更新值并传递给视图样式。

请考虑贡献并使其变得更好。

© www.soinside.com 2019 - 2024. All rights reserved.