사용자는 주로 터치를 통해 모바일 앱과 상호작용한다. 버튼 누르기, 목록 스크롤, 지도 확대/축소 등 다양한 동작을 조합하여 사용할 수 있다.
React Native는 모든 종류의 일반적인 제스처를 처리하는 컴포넌트는 물론 포괄적인 제스처 응답 시스템을 제공하여 고급 제스처 인식을 허용한다.
Button
은 모든 플랫폼에서 렌더링되는 기본 버튼 컴포넌트를 제공한다.
<Button
onPress={() => {
console.log('You tapped the button!')
}}
title="Press Me"
/>
iOS에서는 파란색 라벨이 렌더링되고, Android에서는 밝은 텍스트가 포함된 파란색 둥근 사각형이 렌더링된다.
기본 버튼이 앱에 적합하지 않은 경우 'Touchables' 중 하나를 사용하여 자신만의 버튼을 만들 수 있다.
'Touchables'한 컴포넌트는 두드리는 동작을 캡처하는 기능을 제공하고 동작이 인식되면 피드백을 표시할 수 있다. 그러나 이러한 컴포넌트는 기본 스타일을 제공하지 않으므로 앱에서 보기 좋게 보이도록 약간의 작업을 수행해야 한다.
TouchableHighlight
를 사용할 수 있다. 사용자가 버튼을 누르면 보기의 배경이 어두워진다.TouchableNativeFeedback
을 사용하여 사용자의 터치에 반응하는 잉크 표면 반응 잔물결을 표시하는 것을 고려할 수 있다.TouchableOpacity
는 버튼의 불투명도를 줄여 사용자가 누르고 있는 동안 배경이 보이도록 하여 피드백을 제공하는 데 사용할 수 있다.TouchableWithoutFeedback
을 사용할 수 있다.사용자가 설정된 시간 동안 뷰를 누르고 있는 것을 감지하고 싶은 경우 Tochables 컴포넌트의 props인onLongPress
를 사용할 수 있다.
import {Component} from 'react';
import {
Alert,
Platform,
StyleSheet,
Text,
TouchableHighlight,
TouchableOpacity,
TouchableNativeFeedback,
TouchableWithoutFeedback,
View,
} from 'react-native';
export default class Touchables extends Component {
_onPressButton() {
Alert.alert('You tapped the button!');
}
_onLongPressButton() {
Alert.alert('You long-pressed the button!');
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={this._onPressButton} underlayColor="white">
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableHighlight</Text>
</View>
</TouchableHighlight>
<TouchableOpacity onPress={this._onPressButton}>
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableOpacity</Text>
</View>
</TouchableOpacity>
<TouchableNativeFeedback
onPress={this._onPressButton}
background={
Platform.OS === 'android'
? TouchableNativeFeedback.SelectableBackground()
: undefined
}>
<View style={styles.button}>
<Text style={styles.buttonText}>
TouchableNativeFeedback{' '}
{Platform.OS !== 'android' ? '(Android only)' : ''}
</Text>
</View>
</TouchableNativeFeedback>
<TouchableWithoutFeedback onPress={this._onPressButton}>
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
<TouchableHighlight
onPress={this._onPressButton}
onLongPress={this._onLongPressButton}
underlayColor="white">
<View style={styles.button}>
<Text style={styles.buttonText}>Touchable with Long Press</Text>
</View>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 60,
alignItems: 'center',
},
button: {
marginBottom: 30,
width: 260,
alignItems: 'center',
backgroundColor: '#2196F3',
},
buttonText: {
textAlign: 'center',
padding: 20,
color: 'white',
},
});
일반적으로 터치 가능한 화면이 있는 기기에서 사용되는 동작에는 스와이프와 팬(하나 이상의 손가락을 움직이는 제스처)이 포함된다. 이를 통해 사용자는 항목 목록을 스크롤하거나 컨텐츠 페이지를 스와이프할 수 있다.
모바일 앱은 단일 화면으로 구성되는 경우가 거의 없으므로, 여러 화면의 표시 및 전환 관리는 일반적으로 Navigator로 처리된다.
Navigate를 할 경우 React Navigation
을 사용하는 것이 좋다. React Navigation
은 Android와 iOS 모두에서 일반적인 stack navigation 및 tap navigation 패턴을 제공하는 기능을 갖춘 간단한 Navigate 솔루션을 제공한다.
App.js
를 NavigationContainer
로 감싸야 react-navigation을 사용할 수 있다.
import {NavigationContainer} from '@react-navigation/native';
const App = () => {
return (
<NavigationContainer>
{/* Rest of your app code */}
</NavigationContainer>
);
};
export default App;
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{title: 'Welcome'}}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
이 예에서는 컴포넌트를 사용하여 정의된 Home, Profile 두 개의 화면이 있다.
Stack.Screen
의 options
props에서 각 화면의 화면 제목과 같은 옵션을 설정할 수 있다.
Stack.Screen
각 화면은 React 컴포넌트인 component
props를 사용한다. 이러한 컴포넌트는 다른 화면에 연결하는 다양한 방법이 있는 navigation
이라는 속성을 받는다. 예를 들어 navigation.navigate
를 사용하여 Profile 화면으로 이동할 수 있다.
const HomeScreen = ({navigation}) => {
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigation.navigate('Profile', {name: 'Jane'})
}
/>
);
};
const ProfileScreen = ({navigation, route}) => {
return <Text>This is {route.params.name}'s profile</Text>;
};
제스처 응답 시스템은 앱의 제스처 라이프사이클을 관리한다. 앱이 사용자의 의도를 결정함에 따라 터치는 여러 단계를 거칠 수 있다.
예를 들어 앱은 터치가 스크롤되는지, 위젯에서 슬라이딩되는지 또는 탭하는지 확인해야 한다. 터치하는 동안에도 변경될 수 있으며, 동시에 여러 터치가 있을 수도 있다.
컴포넌트가 부모 또는 자식 컴포넌트에 대한 추가 지식 없이 이러한 터치 상호 작용들을 협상할 수 있도록 하려면 터치 응답 시스템이 필요하다.
응답자 시스템은 사용하기 복잡할 수 있다. 따라서 React Native는 'tappable'해야 하는 것들에 대한 추상적인 Touchable
구현을 제공한다. 이것은 응답자 시스템을 사용하고 탭 상호작용을 선언적으로 구성할 수 있게 해준다.
웹에서 버튼이나 링크를 사용하는 곳이라면 어디서나 TouchableHighlight
를 사용할 수 있다.
view는 올바른 협상 방법을 구현함으로써 터치 responder가 될 수 있다. veiw가 responder가 되기를 원하는지 묻는 두 가지 방법이 있다.
View.props.onStartShouldSetResponder: evt => true
: view가 터치 시작 시 responder가 되기를 원하는지?View.props.onMoveShouldSetResponder: evt => true
: responder가 아닌 경우 view의 모든 터치 동작을 요청한다. 이 view가 터치 응답성을 '주장'하기를 원하는지?view가 true를 반환하고 responder가 되려고 한다면, 다음 중 하나가 발생한다.
View.props.onResponderGrant: evt => {}
: view는 이제 터치 이벤트에 응답한다. 지금 무슨 일이 일어나고 있는지 사용자에게 강조하고 보여줄 때 이다.View.props.onResponderReject: evt => {}
: 다른 것이 지금 responder이므로 해제하지 않는다.view가 응답하는 경우 다음 핸들러를 호출할 수 있다.
View.props.onResponderMove: evt => {}
: 사용자가 손가락을 움직이고 있다.View.props.onResponderRelease: evt => {}
: 터치가 끝나면 실행된다. 즉 'touchUp'이다.View.props.onResponderTerminationRequest: evt => true
: 다른 것이 responder가 되길 원한다. 이 view가 responder를 해제해야 하는지? true를 반환하면 해제가 허용된다.View.props.onResponderTerminate: evt => {}
: responder를 view에서 가져온다. onResponderTerminationRequest
호출 후 다른 view에서 가져오거나 묻지 않고 OS에서 가져갈 수 있다. (iOS의 제어센터, 알림센터에서 발생)evt
는 다음과 같은 형태의 합성 터치 이벤트이다.
nativeEvent
changedTouches
: 마지막 이벤트 이후 변경된 모든 터치 이벤트 배열identifier
: 터치의 IDlocationX
: 요소를 기준으로 한 터치의 X 위치locationY
: 요소를 기준으로 한 터치의 Y 위치pageX
: 루트 요소를 기준으로 한 터치의 X 위치pageY
: 루트 요소를 기준으로 터치의 Y 위치target
: 터치 이벤트를 수신하는 요소의 노드 IDtimestamp
: 속도 계산에 유용한 터치 시간 식별자touches
: 화면의 모든 현재 터치 배열onStartShouldSetResponder
및 onMoveShouldSetResponder
는 가장 깊은 노드가 먼저 호출되는 버블링 패턴으로 호출된다. 즉, ShouldSetResponder
핸들러에 대해 여러 view가 true를 반환하면 가장 깊은 컴포넌트가 responder가 된다. 이렇게 하면 모든 컨트롤과 버튼을 사용할 수 있으므로 대부분의 경우 바람직하다.
그러나 부모나 responder가 되길 원할 수도 있는데, 이는 캡처 단계를 사용하여 처리할 수 있다. 응답자 시스템이 가장 깊은 컴포넌트에서부터 버블링되기 전에 캡처 단계를 수행하고 ShouldSetResponderCapture
가 실행된다. 따라서 상위 view가 터치 시작 시 하위 view가 responder가 되는 것을 방지하려면 true를 반환하는 onStartShouldSetResponderCapture
핸들러가 있어야 한다.
View.props.onStartShouldSetResponderCapture: evt => true
View.props.onMoveShouldSetResponderCapture: evt => true