React Native, dismiss keyboard

모바일 앱에서 흔히 볼 수 있는 UX 중에 빈 공간을 탭 시 키보드가 내려가는 액션이 있다.

보통 안드로이드 폰에서는 키보드가 올라와 있는 상태에서 back 버튼을 탭하면 내려가는 것이 일반적이다. 하지만 아이폰에는 back 버튼을 디바이스에서 지원하지 않기 때문에 보통 텍스트 필드의 포커스가 사라지는 blur 시점이나 빈 공간 탭 시 키보드를 내린다.

React Native에서 안드로이드 폰, 아이폰 모두 동일한 UX로 현재 텍스트 필드 외 빈 공간을 탭할 경우 올라와 있는 키보드를 내리는 액션을 제공하기 위해서는 어떻게 해야할까?

물론 Android, iOS 각각 네이티브로 기능을 개발하고 React Native에서 호출하는 방법이 있다. 하지만 Android, iOS의 네이티브 지식을 최소한으로 사용하여 앱을 생산할 수 있는 React Native의 장점을 극대화하기 위해 React Native만의 방법을 찾고자 했다.

결과 화면은 다음과 같다. 텍스트 필드를 탭 시 키보드가 노출, 빈 공간을 탭 시 키보드 내림, 버튼 탭 시 키보드 내림 액션이 일어나도록 했다.

dismiss keyboard on react native

코드는 다음과 같다. Keyboard.dismiss()를 키보드를 내릴 때 사용하는 빈 공간을 탭하는 이벤트를 리스닝하기 위해 TouchableWithoutFeedback를 사용한 부분에 주목해야 할 필요가 있다. TouchableWithoutFeedback는 표현 그대로 탭한 객체에 아무런 피드백을 주지 않고 이벤트만 리스닝하는 역할을 한다.

컨테이너 뷰를 TouchableWithoutFeedback로 감싸면 그 위에 올리는 객체 외 빈 공간들을 탭할 때 일어나는 이벤트를 리스닝할 수 있게 된다.

버튼을 탭할 때 별도의 이벤트를 _onPressSubmit으로 리스닝하는 부분을 보면 TouchableWithoutFeedback의 영향을 받지 않고 터치 객체 자신의 이벤트를 받을 수 있다는 점을 알 수 있다.

import React, {Component} from "react";
import {
AppRegistry,
StyleSheet,
Text, View,
TextInput,
Keyboard,
TouchableWithoutFeedback,
TouchableOpacity} from "react-native";
export default class KeyboardTest extends Component {
constructor(props) {
super(props);
this.state = {
keyboard: false
};
}
  getComment = () => {
return this.state.keyboard ? 'Keyboard is showing!' : 'Keyboard is dismissed!';
};
  _onPressEmptySpace = () => {
Keyboard.dismiss();
this.setState({
keyboard: false
});
}
  _onPressSubmit = () => {
Keyboard.dismiss();
this.setState({
keyboard: false
});
}
  _onFocusTextField = () => {
this.setState({
keyboard: true
});
}
  render() {
return (
<TouchableWithoutFeedback onPress={this._onPressEmptySpace}>
<View style={styles.container}>
<View style={styles.textContainer}>
<Text style={styles.textBig}>
{this.getComment()}
</Text>
</View>
<TextInput style={[styles.textField, styles.textMedium]}
onFocus={this._onFocusTextField}
placeholder="Type here!"/>
<TouchableOpacity onPress={this._onPressSubmit}>
<View style={styles.buttonContainer}>
<Text style={styles.textOverlay}>
Submit
</Text>
</View>
</TouchableOpacity>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'bisque'
},
textContainer: {
alignItems: 'center'
},
textMedium: {
fontSize: 14,
color: 'orangered'
},
textBig: {
fontSize: 18,
color: 'orangered'
},
textOverlay: {
fontSize: 14,
color: 'white'
},
textField: {
height: 40,
backgroundColor: 'white',
borderColor: 'tomato',
borderWidth: 1,
marginHorizontal: 20,
marginVertical: 10,
paddingHorizontal: 10
},
buttonContainer: {
height: 40,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'tomato',
marginHorizontal: 20
}
});
AppRegistry.registerComponent('KeyboardTest', () => KeyboardTest);

주의할 점이 있는데 React Native v0.36 이하는 Keyboard 컴포넌트가 존재하지 않았다. 따라서 위와 같이 코딩할 수 없고 다음과 같이 바로 라이브러리에서 참조해야 한다.

const keyboardDismiss = require('dismissKeyboard');
...
_onPressEmptySpace = () => {
keyboardDismiss();
this.setState({
keyboard: false
});
}
...

Jason은 React Native에 관련하여 제작한 라이브러리나 기법이 쌓이고 있으나 글을 쓰는 일은 생각보다 시간을 많이 소비하기 때문에 미루고 있다. 글을 꾸준히 올리는 사람들이 꽤 부지런한 사람들이라 생각하게 되었다.