BottomNavigation은 하단에 버튼을 두어 버튼 클릭 시 원하는 화면으로 넘어갈 수 있게끔 하는 네비게이션입니다.
위의 플래시 이미지가 바텀네비게이션입니다.
저는 BottomNavigation을 메인페이지에 둘 것이니 LoginScreen, RegisterScreen이 있는 StackNavigation에 HomeScreen 대신 BottomNavigation을 두도록 하겠습니다.
다음의 명령어로 패키지를 설치하겠습니다. 그리고 아이콘 패키지도 설치하겠습니다.
npm install --save @react-navigation/bottom-tabs
npm install --save react-native-vector-icons/Ionicons
그리고 다음의 navigator디렉토리에 BottomNavigation.js를 만들어 네비게이션을 구현하겠습니다.
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
import palette from '../styles/palette';
import HomeScreen from '../pages/home/HomeScreen';
import PostScreen from '../pages/post/PostScreen';
import MapScreen from '../pages/map/MapScreen';
import MyPageScreen from '../pages/user/MyPageScreen';
const Tab = createBottomTabNavigator();
const BottomNavigation = ({ route }) => {
return(
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color }) => {
var iconName;
var iconSize;
if(route.name === "Home") {
iconName = focused ? 'ios-home' : 'ios-home-outline';
iconSize = focused ? 32 : 24;
} else if (route.name === 'Board') {
iconName = focused ? 'ios-reader' : 'ios-reader-outline';
iconSize = focused ? 32 : 24;
} else if (route.name === 'Map'){
iconName = focused ? 'ios-location' : 'ios-location-outline';
iconSize = focused ? 32 : 24;
} else if (route.name === 'Setting'){
iconName = focused ? 'ios-person' : 'ios-person-outline';
iconSize = focused ? 32 : 24;
}
return (
<Icon
size={ iconSize }
name={ iconName }
color={ color }
/>
)
}
})}
tabBarOptions = {{
activeTintColor: palette.blue[4],
inactiveTintColor: palette.gray[5],
}}
>
<Tab.Screen
name="Home"
children={
() => <HomeScreen />
}
/>
<Tab.Screen
name="Post"
children={
() => <PostScreen />
}
/>
<Tab.Screen
name="Map"
children={
() => <MapScreen />
}
/>
<Tab.Screen
name="MyPage"
children={
() => <MyPageScreen />
}
/>
</Tab.Navigator>
);
};
export default BottomNavigation;
import React from 'react';
import 'react-native-gesture-handler';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import LoginScreen from '../pages/auth/LoginScreen';
import RegisterScreen from '../pages/auth/RegisterScreen';
import BottomNavigation from './BottomNavigation';
const Stack = createStackNavigator();
const StackNavigatior = () => {
return(
<NavigationContainer>
<Stack.Navigator>
...
<Stack.Screen
name="Tab"
component={ BottomNavigation }
options={{
headerShown: false
}}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default StackNavigatior;
import React from 'react';
import { View, StyleSheet } from 'react-native';
import StyledBorderButton from '../../../styles/common/StyledBorderButton';
import StyledFullButton from '../../../styles/common/StyledFullButton';
import StyledTextInput from '../../../styles/common/StyledTextInput';
import palette from '../../../styles/palette';
const LoginForm = ({ navigation }) => {
...
const onPressMainNavigator = e => {
e.preventDefault();
navigation.navigate('Tab');
};
return(
<View style={ styles.loginBox }>
...
<StyledFullButton
onPress={ onPressMainNavigator }
text="Sign in"
/>
...
</View>
);
};
...
export default LoginForm;
위와 같이 코드를 작성하고 테스트 화면을 보겠습니다.
아이콘과 바텀네비게이션이 잘 작동하는 모습을 보실 수 있습니다.
그러면 HomeScreen의 화면을 구현해보도록 하겠습니다. HomeScreen에서는 종목별로 카테고리를 만들어 종목 아이콘을 클릭하면 그와 관련된 운동 장소를 맵에 띄울 수 있도록 하는 기능을 가지고 있습니다.
import React from 'react';
import {
StyleSheet,
View
} from 'react-native';
import CategoryIcon from '../../../styles/common/CategoryIcon';
import ImageIcon from '../../../styles/common/ImageIcon';
import palette from '../../../styles/palette';
const HomeContent = () => {
return(
<View style={ styles.container }>
<View style={ styles.categoryLine }>
<CategoryIcon
name={ "ios-baseball-outline" }
text={ "야구" }
type_nm={ "야구장" }
/>
<CategoryIcon
name={ "ios-basketball-outline" }
text={ "농구" }
type_nm={ "생활체육관" }
/>
<CategoryIcon
name={ "ios-basketball" }
text={ "야외농구" }
type_nm={ "농구장" }
/>
<CategoryIcon
name={ "ios-football-outline" }
text={ "축구" }
type_nm={ "축구장" }
/>
</View>
<View style={ styles.categoryLine }>
<ImageIcon
name={ "arrow" }
text={ "양궁" }
type_nm={ "국궁장" }
/>
<ImageIcon
name={ "badminton" }
text={ "배드민턴" }
type_nm={ "배드민턴장" }
/>
<ImageIcon
name={ "climb" }
text={ "클라이밍" }
type_nm={ "클라이밍장" }
/>
<ImageIcon
name={ "gateball" }
text={ "게이트볼" }
type_nm={ "게이트볼장" }
/>
</View>
<View style={ styles.categoryLine }>
<ImageIcon
name={ "ice_skate" }
text={ "스케이트" }
type_nm={ "빙상장" }
/>
<ImageIcon
name={ "inline_skate" }
text={ "인라인" }
type={ "인라인스케이트장" }
/>
<ImageIcon
name={ "pingpong" }
text={ "탁구" }
type_nm={ "탁구장" }
/>
<ImageIcon
name={ "sepak_takraw" }
text={ "족구" }
type_nm={ "족구장" }
/>
</View>
<View style={ styles.categoryLine }>
<ImageIcon
name={ "shoot" }
text={ "사격" }
type_nm={ "사격장" }
/>
<ImageIcon
name={ "swim" }
text={ "수영" }
type_nm={ "수영장" }
/>
<ImageIcon
name={ "volleyball" }
text={ "배구" }
type_nm={ "구기체육관" }
/>
<ImageIcon
name={ "volleyball" }
text={ "야외배구" }
type_nm={ "배구장" }
/>
</View>
<View style={ styles.categoryLine }>
<CategoryIcon
name={ "ios-bicycle" }
text={ "자전거" }
type_nm={ "산악자전거장" }
/>
<CategoryIcon
name={ "ios-school" }
text={ "학교체육" }
type_nm={ "학교체육시설" }
/>
<CategoryIcon
name={ "ios-football" }
text={ "풋살" }
type_nm={ "풋살장" }
/>
<ImageIcon
name={ "tennis" }
text={ "테니스" }
type_nm={ "테니스장" }
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
marginTop: 30,
width: 420,
backgroundColor: palette.white[0],
},
containerTitle: {
alignItems: 'center',
justifyContent: 'center',
},
textTitle: {
fontWeight: 'bold',
fontSize: 20,
},
categoryLine: {
flexDirection: 'row',
width: 420,
height: 70,
marginBottom: 20,
},
});
export default HomeContent;
카테고리 아이콘들이 들어갈 컴포넌트입니다. 아이콘들을 만들어 보도록 하겠습니다.
import React from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import palette from '../palette';
const CategoryIcon = ({
name,
text
}) => {
return(
<View style={ styles.container }>
<TouchableOpacity>
<Icon
size={ 48 }
name={ name }
color={ palette.blue[4] }
/>
</TouchableOpacity>
<Text style={ styles.font }>
{ text }
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
width: 60,
height: 60,
margin: 20,
},
font: {
fontWeight: 'bold',
},
});
export default CategoryIcon;
import React from 'react';
import {
StyleSheet,
View,
TouchableOpacity,
Image,
Text
} from 'react-native';
import palette from '../palette';
import arrow from '../../assets/img/arrow.png';
import badminton from '../../assets/img/badminton.png';
import climb from '../../assets/img/climb.png';
import gateball from '../../assets/img/gateball.png';
import ice_skate from '../../assets/img/ice_skate.png';
import inline_skate from '../../assets/img/inline_skate.png';
import pingpong from '../../assets/img/pingpong.png';
import sepak_takraw from '../../assets/img/sepak_takraw.png';
import shoot from '../../assets/img/shoot.png';
import swim from '../../assets/img/swim.png';
import volleyball from '../../assets/img/volleyball.png';
import tennis from '../../assets/img/tennis.png';
const ImageIcon = ({ name, text }) => {
if(name === "arrow") name = arrow
else if(name === "badminton") name = badminton
else if(name === "climb") name = climb
else if(name === "gateball") name = gateball
else if(name === "ice_skate") name = ice_skate
else if(name === "inline_skate") name = inline_skate
else if(name === "pingpong") name = pingpong
else if(name === "sepak_takraw") name = sepak_takraw
else if(name === "shoot") name = shoot
else if(name === "swim") name = swim
else if(name === "volleyball") name = volleyball
else if(name === 'tennis') name = tennis;
return(
<View style={ styles.container }>
<TouchableOpacity>
<Image
style={ styles.icon }
source={ name }
/>
</TouchableOpacity>
<Text style={ styles.font }>
{ text }
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
width: 60,
height: 60,
margin: 20,
},
icon: {
tintColor: palette.blue[4],
},
font: {
fontWeight: 'bold',
},
});
export default ImageIcon;
ImageIcon은 직접 이미지 파일을 다운받아 만들어 놓은 것이기 때문에 따로 만들었습니다. 이미지, 폰트에 대한 파일들은 깃에 올려놓겠습니다.
https://github.com/biuea3866/MyLifeSports
그리고 HomeScreen의 헤더 부분을 다음과 같이 변경하겠습니다.
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
import palette from '../styles/palette';
import HomeScreen from '../pages/home/HomeScreen';
import PostScreen from '../pages/post/PostScreen';
import MapScreen from '../pages/map/MapScreen';
import MyPageScreen from '../pages/user/MyPageScreen';
const Tab = createBottomTabNavigator();
const BottomNavigation = ({ route }) => {
return(
...
<Tab.Screen
name="Home"
options={{
title: 'Life Sport',
tabBarLabel: 'Home',
headerStyle: {
backgroundColor: palette.blue[4],
},
headerTintColor: palette.white[0],
headerTitleStyle: {
fontWeight: 'bold'
},
}}
children={
() => <HomeScreen />
}
/>
...
};
export default BottomNavigation;
결과 화면입니다.
잘 나온 모습을 볼 수 있습니다. 이런 방식으로 게시글 페이지, 맵 페이지, 마이 페이지 부분을 만들어 보도록 하겠습니다.