React Native踩坑指南:ios鍵盤遮擋輸入框

qiushijie發表於2019-04-03

在react native開發中第一個最容易遇到的坑就是點選輸入框,ios的鍵盤遮擋住了輸入框,看不到輸入的內容。而直接使用第三方的庫解決這個問題的過程中又遇到了新的問題,github上開發的庫無法解決問題。所以就只能自行封裝一個來解決鍵盤遮擋輸入框的問題。

原始碼連結KeyboardAvoidingView.tsx

import React from 'react';
import {Animated, Dimensions, EmitterSubscription, Keyboard, Platform, TextInput, UIManager} from 'react-native';
import {NavigationEventSubscription, NavigationScreenProp} from 'react-navigation';

interface IState {
  height: Animated.Value;
}

export interface KeyboardAvoidingViewProps {
  navigation: NavigationScreenProp<any, any>;
  onInputChange?: (onInput: boolean) => void;
  // 固定鍵盤高度
  fixedKeyboardHeight?: boolean;
  // 固定上浮高度
  fixedHeight?: number;
  // 鍵盤加上高度
  addHeight?: number;
}

export default class KeyboardAvoidingView extends React.Component<KeyboardAvoidingViewProps, IState> {
  state = {
    height: new Animated.Value(0),
    onInput: false
  }
  private didFocus?: NavigationEventSubscription;
  private willBlur?: NavigationEventSubscription;
  private keyboardDidShowSub?: EmitterSubscription;
  private keyboardDidHideSub?: EmitterSubscription;
  componentDidMount() {
    this.didFocus = this.props.navigation.addListener('didFocus', () => {
      this.keyboardDidShowSub = Keyboard.addListener('keyboardDidShow', this.handleKeyboardDidShow);
      this.keyboardDidHideSub = Keyboard.addListener('keyboardDidHide', this.handleKeyboardDidHide);
    });
    this.willBlur = this.props.navigation.addListener('willBlur', () => {
        this.keyboardDidShowSub && this.keyboardDidShowSub.remove();
        this.keyboardDidHideSub && this.keyboardDidHideSub.remove();
    });
  }
  handleKeyboardDidShow = (event: any) => {
    this.props.onInputChange && this.props.onInputChange(true);
    if (Platform.OS != 'ios') return;
    console.log('handleKeyboardDidShow');
    const { height: windowHeight } = Dimensions.get('window');
    const addHeight = this.props.addHeight || 0;
    const keyboardHeight = event.endCoordinates.height + addHeight;
    const currentlyFocusedField = TextInput.State.currentlyFocusedField();
    console.log('currentlyFocusedField', currentlyFocusedField);
    if (currentlyFocusedField == null) return;
    if (this.props.fixedHeight != undefined) {
      Animated.timing(
        this.state.height,
        {
          toValue: -this.props.fixedHeight,
          duration: 200,
          useNativeDriver: true,
        }
      ).start();
      return;
    }
    if (this.props.fixedKeyboardHeight) {
      console.log('keyboardHeight', keyboardHeight);
      Animated.timing(
        this.state.height,
        {
          toValue: -keyboardHeight,
          duration: 200,
          useNativeDriver: true,
        }
      ).start();
      return;
    }
    UIManager.measure(currentlyFocusedField, (originX, originY, width, height, pageX, pageY) => {
      const fieldHeight = height;
      const fieldTop = pageY;
      const gap = (windowHeight - keyboardHeight) - (fieldTop + fieldHeight);
      console.log('windowHeight', windowHeight, 'keyboardHeight', keyboardHeight, 'gap', gap, 'fieldTop', fieldTop, 'fieldHeight', fieldHeight);
      if (gap >= 0) {
        return;
      }
      Animated.timing(
        this.state.height,
        {
          toValue: gap,
          duration: 200,
          useNativeDriver: true,
        }
      ).start();
    });
  }
  handleKeyboardDidHide = () => {
    this.props.onInputChange && this.props.onInputChange(false);
    if (Platform.OS != 'ios') return;
    console.log('handleKeyboardDidHide');
    Animated.timing(
      this.state.height,
      {
        toValue: 0,
        duration: 200,
        useNativeDriver: true,
      }
    ).start();
  }
  render() {
    if (Platform.OS == 'ios') {
      return (
        <Animated.View style={{flex: 1, transform: [{translateY: this.state.height}]}}>
          {this.props.children}
        </Animated.View>
      );
    }
    return this.props.children;
  }
}
複製程式碼

需要注意的是KeyboardAvoidingView與react-navigation配合使用,在路由push後事件還在監聽,所以需要移除,然後等路由重新回來到當前頁面再重新監聽。

將元件包裹在檢視最上層即可

<KeyboardAvoidingView navigation={this.props.navigation}>
  <View>
    <TextInput />
    ...
  </View>
</KeyboardAvoidingView>
複製程式碼

相關文章