我的应用程序中有一个页面,其中两个 TextInput 内部视图覆盖整个屏幕。
两个 TextInput 有一个 ref
ref={refTextInput1}
、ref={refTextInput2}
。
他们还有一个 onSubmitEditing onSubmitEditing={() => refTextInput2()}
,onSubmitEditing={() => refTextInput1()}
。
因此,当我按键盘上的“返回”时,焦点将切换到所需的输入,但它也会使整个屏幕跳转/故障,这真的很烦人,也是不想要的。
焦点应该切换到所需的输入,而不会使整个屏幕跳跃/故障。
我在 StackOverflow 和 Github Issues 上到处搜索,但没有发现很多与这个特定问题相关的问题。 起初,我以为我遇到了类似于 #30207 的问题,但经过一段时间尝试查找问题后,我想我找到了它。
问题仅在“设置”>“密码”>“自动填充密码”中启用“自动填充密码”时才会出现(我认为)。 如果我禁用自动填充密码,则不会发生跳转/故障。
我还注意到,在启用自动填充密码的情况下,键盘事件总是在焦点上触发两次,而在禁用自动填充密码的情况下,仅在焦点上触发一次。也可能有关系。 (运行小吃示例时请参阅控制台。)
我查看了其他应用程序,它们在同一类型的登录屏幕中看起来都很好。例如 Instagram 就没有这个问题。
这也可能是我制作屏幕的方式,如果有更好的方法来制作相同或相似的屏幕,我会虚心接受建议。
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
我还注意到,在启用自动填充密码的情况下,键盘事件总是在焦点上触发两次,而在禁用自动填充密码的情况下,仅在焦点上触发一次。也可能有关系。 (运行小吃示例时请参阅控制台。)
按键盘上的“返回”时,焦点应切换到所需的输入,而不会导致整个屏幕跳跃/毛刺。就像在“设置”>“密码”>“自动填充密码”中禁用自动填充密码时一样。
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 被暂时禁用,问题仍然存在)
我也遇到了同样的问题,但是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};
我还添加了一个动画,但它是可选的,您可以使用状态来更新值并传递给视图样式。
请考虑贡献并使其变得更好。