개발/오늘 배운 지식

[React Native] 리액트 네이티브 키보드 내용 가림 방지

Woogie2 2023. 1. 5. 16:33
반응형

리액트 네이티브 채팅방 구현하기

요즘 리액트 네이티브를 활용해서 채팅 기능을 구현하고 있다.

그 중에서도 Android와는 달리 iOS에서 키보드가 입력창을 가리는 현상 때문에 고생이 많았다. 그 문제를 해결하던 과정을 기록하였다.

KeyboardAvoidingView

이 이슈에 대해 검색을 해보면 먼저 이 KeyboardAvoidingView가 가장 먼저 결과로 나올 것이다. 그래서 처음에는 이 View를 활용해서 구현했다. 하지만, 문제점이 있었다. 입력창은 키보드 위로 잘 밀려올라가지만, 채팅 내용은 그대로 있다는 점이다. 다시 원래 보고 있던 채팅을 보려면 스크롤을 다시 해야한다는 단점이 느껴져서 해결하고자 노력했다.

링크

import React from 'react';
import { KeyboardAvoidingView, Platform, StyleSheet } from 'react-native';
import { useHeaderHeight } from '@react-navigation/elements';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

type AvoidingViewProps = {
  children: React.ReactNode;
};

const TextInputAvoidingView = ({ children }: AvoidingViewProps) => {
  const headerHeight = useHeaderHeight();
  const inset = useSafeAreaInsets();
  return Platform.OS === 'ios' ? (
    <KeyboardAvoidingView
      style={[styles.wrapper, { marginBottom: inset.bottom }]}
      behavior="padding"
      keyboardVerticalOffset={headerHeight}>
      {children}
    </KeyboardAvoidingView>
  ) : (
    <>{children}</>
  );
};

export default TextInputAvoidingView;

const styles = StyleSheet.create({
  wrapper: {
    flex: 1,
  },
});

그래서 다른 오픈소스 Chat UI에서는 어떻게 해결하는지 파악해봤다.

해결의 실마리는 링크에서 찾을 수 있었다.

KeyboardAccessoryView라는 커스텀 컴포넌트로 감싸서 해결하기에 해당 레포로 이동해보니 리액트 네이티브 레포의 풀리퀘스트로 안내하였다. 링크

결론: ScrollView or FlatList + InputAccessoryView

해당 풀리퀘스트로 이동해보면, 아래와 같이 리액트 네이티브에서 제공하는 View만으로 해결이 가능하게 업데이트가 되었다는 이야기이다.

export const ChatPage = ({
  flatListProps,
  textInputProps
}: Props): React.ReactElement => (
  <>
    <FlatList
      {...flatListProps}
      keyboardDismissMode="interactive"
      automaticallyAdjustContentInsets={false}
      contentInsetAdjustmentBehavior="never"
      maintainVisibleContentPosition={{ minIndexForVisible: 0, autoscrollToTopThreshold: 100 }}
      automaticallyAdjustKeyboardInsets={true}
    />
    <InputAccessoryView backgroundColor={colors.white}>
      <ChatInput {...textInputProps} />
    </InputAccessoryView>
  </>
);

iOS의 경우 Input 컴포넌트를 InputAccessoryView로 감싸주면, 키보드의 위쪽에 붙어있는 View를 구현할 수 있었고, 안드로이드의 경우 자동적으로 적용이 되었다. 따라서 최종적인 코드는 아래와 같이 OS 별로 구별해주어야 한다.

<View style={[styles.ChatRoomContainer, { marginBottom: inset.bottom }]}>
      <>
        <FlatList
          contentContainerStyle={styles.contentContainer}
          data={messages}
          renderItem={renderItem}
          automaticallyAdjustContentInsets={false}
          inverted={true}
          keyboardDismissMode="interactive"
          keyboardShouldPersistTaps="handled"
          contentInsetAdjustmentBehavior="never"
          maintainVisibleContentPosition={{
            minIndexForVisible: 0,
            autoscrollToTopThreshold: 80,
          }}
          automaticallyAdjustKeyboardInsets={true}
        />
        {Platform.OS === 'ios' ? (
          <InputAccessoryView>
            <InputToolbar />
          </InputAccessoryView>
        ) : (
          <InputToolbar />
        )}
      </>
    </View>

이제는 머리 아프게 KeyboardAvoidingView 혹은 써드파티 라이브러리를 사용하지 않아도 된다!

 

결과물

iOS

Android

 

반응형