얼렁뚱땅

[React Native] Tab Navigation 사용하기 본문

React Native

[React Native] Tab Navigation 사용하기

당당익명 2021. 8. 12. 00:52

Tab Navigation을 위한 라이브러리 설치

npm i @react-navigation/bottom-tabs

파일, 폴더 구성 (기본 뼈대 구성)

 

src 밖 App.js -> src 안 App.js -> navigations 폴더의 index.js -> MainTab.js -> Mainscreen.js, Mypage.js, Instagram.js 페이지 가져오기!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

src 밖 App.js

import App from './src/App';

export default App;

모든 코드를 src 폴더 내에서 관리하는 것이 편해서 이렇게 했다.

 

src/App.js

import React from 'react';
import Navigation from './navigations';

export default function App() {
  return <Navigation />;
}

이상하게 <View> component 안에 <Navigation>을 두면 화면이 아무것도 안나타난다. 그래서 일단은 임시로 이렇게 두었다. 나중에 <ThemeProvider>를 사용할 예정. <Navigation> 컴포넌트 가져오기.

 

navigations/index.js

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import MainTab from './MainTab';

const Navigation = () => {
  return (
    <NavigationContainer>
      <MainTab />
    </NavigationContainer>
  );
};

export default Navigation;

반드시 <NavigationContainer>로 감싸주어야 한다. <MainTab> 컴포넌트 가져오기.

 

navigations/MainTab.js

import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Mainscreen from '../screens/Mainscreen';
import Mypage from '../screens/Mypage';
import Instagram from '../screens/Instagram';

const Tab = createBottomTabNavigator();

const MainTab = () => {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Mainscreen" component={Mainscreen} />
      <Tab.Screen name="Mypage" component={Mypage} />
      <Tab.Screen name="Instagram" component={Instagram} />
    </Tab.Navigator>
  );
};

export default MainTab;

3개의 tab을 만든다.

 

결과물


Tab Bar 버튼 아이콘 설정하기

탭 버튼에 아이콘을 렌더링하기 위해서는 tabBarIcon을 이용해야 한다. 

 

vector-icons는 Expo 프로젝트에서 기본적으로 설치되는 라이브러리이다.

여기에서 아이콘을 확인할 수 있다 : https://icons.expo.fyi/

 

@expo/vector-icons directory

 

icons.expo.fyi

Filters

Filters를 눌러보면 다양한 아이콘 종류가 있는 것을 확인 할 수 있다. 나는 이 프로젝트에서 MaterialCommunityIcons만을 사용했지만 선호도에 따라 자유롭게 선택할 수 있다. 대신 import를 꼭 해주어야 한다.

 

총 두가지 방식으로 아이콘을 설정할 수 있다.

  1. Screen 컴포넌트마다 options에서 관리하는 방법
  2. Navigator 컴포넌트에서 screenOptions를 사용해 한번에 관리하는 방법

나는 2번째 방법을 사용할 것이다. 

 

navigations/MainTab.js

import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import Mainscreen from '../screens/Mainscreen';
import Mypage from '../screens/Mypage';
import Instagram from '../screens/Instagram';

const TabIcon = ({ name, size, color }) => {
  return <MaterialCommunityIcons name={name} size={size} color={color} />;
};

const Tab = createBottomTabNavigator();

const MainTab = () => {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: (props) => {
          let name = '';
          if (route.name === 'Mainscreen') name = 'home-variant-outline';
          else if (route.name === 'Mypage') name = 'account-circle-outline';
          else name = 'instagram';
          return TabIcon({ ...props, name });
        },
      })}
    >
      <Tab.Screen name="Mainscreen" component={Mainscreen} />
      <Tab.Screen name="Mypage" component={Mypage} />
      <Tab.Screen name="Instagram" component={Instagram} />
    </Tab.Navigator>
  );
};

export default MainTab;

Tab.Navigator의 screenOptions 속성에서 tabBarIcon 속성을 사용.

 

tabBarIcon에 컴포넌트를 반환하는 함수를 지정하면 버튼의 아이콘이 들어갈 자리에 해당 컴포넌트를 렌더링한다.

tabBarIcon에 설정된 함수에는 color, size, focused 값을 포함한 객체가 파라미터로 전달된다.

screenOptions에 객체를 반환하는 함수를 설정하고 함수로 전달되는 route를 이용한다.

Screen 컴포넌트의 name 속성을 값으로 갖는 route의 name값에 따라 렌더링 되는 아이콘이 변경된다.

 

아래는 같은 결과를 내지만 조금 다른 방식으로 짠 코드이다. (2번째 방식)

<Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          let iconName;
          if (route.name == "MainScreen") {
            iconName = "md-home-outline";
          } else if (route.name == "Instagram") {
            iconName = "logo-instagram";
          } else if (route.name == "MyPage") {
            iconName = "ios-person-circle-outline";
          }
          return <Ionicons name={iconName} size={24} color="black" />;
        },
      })}
    >

결과물

 

  1. Screen 컴포넌트마다 options에서 관리하는 방법 --> 이 방식도 뒤에서 쓰이기 때문에 코드를 추가해둔다.
<Tab.Navigator>
      <Tab.Screen
        name="Mainscreen"
        component={Mainscreen}
        options={{
          tabBarIcon: (props) => TabIcon({...props, name: 'home-variant-outline'}),
        }}
      />

나머지 screen도 동일한 방식으로 작성할 수 있다.


Tab Bar 라벨 설정하기

label은 screen 컴포넌트 name값을 기본으로 사용한다. 

  • 라벨을 변경하고 싶은 경우 : tabBarLabel을 통해 변경 가능하다.
<Tab.Screen
        name="Mainscreen"
        component={Mainscreen}
        options={{ tabBarLabel: 'test' }}
      />

 

  • 만약 라벨을 아이콘 옆에 렌더링하고 싶은 경우 : tabBarLabelPosition 사용

tabBarLabelPosition은 below-icon과 beside-icon 값만 가능하다.

screenOptions 안에 넣어준다. (tabBarOptions 안에 넣어주면 deprecated가 뜬다.)

Bottom Tab Navigator: 'tabBarOptions' is deprecated. Migrate the options to 'screenOptions' instead.

    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: (props) => {
          let name = '';
          if (route.name === 'Mainscreen') name = 'home-variant-outline';
          else if (route.name === 'Mypage') name = 'account-circle-outline';
          else name = 'instagram';
          return TabIcon({ ...props, name });
        },
        tabBarLabelPosition: 'beside-icon',
      })}
    >

결과물

 

  • 만약 아이콘만 렌더링하고 싶은 경우 : tabBarShowLabel : false 사용

똑같이 screenOptions 안에 넣어준다. 

    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: (props) => {
          let name = '';
          if (route.name === 'Mainscreen') name = 'home-variant-outline';
          else if (route.name === 'Mypage') name = 'account-circle-outline';
          else name = 'instagram';
          return TabIcon({ ...props, name });
        },
        tabBarShowLabel: false,
      })}
    >

결과물


Tab Bar 스타일 설정하기

tabBarOptions는 deprecated 이므로 모든 속성은 screenOptions에 넣어준다. 그리고 기존에 사용했던 속성 이름 앞에 'tabBar'을 모두 추가해준다. 

  • 배경색을 변경하고 싶은 경우 : tabBarBackgroundColor, tabBarActiveBackgroundColor(터치시 배경)
  • 메인 화면과 탭 바의 경계선을 변경하고 싶은 경우 : tabBarBorderTopColor, tabBarBorderTopWidth
  • 활성화 된 아이콘 색 변경하고 싶은 경우 : tabBarActiveTintColor
  • 비활성화 된 아이콘 색 변경하고 싶은 경우 : tabBarInactiveTintColor

기본값 : 배경은 흰색, 터치시 글씨와 아이콘은 파란색, 비터치시 글씨와 아이콘은 회색

 

<Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: (props) => {
          let name = '';
          if (route.name === 'Mainscreen') name = 'home-variant-outline';
          else if (route.name === 'Mypage') name = 'account-circle-outline';
          else name = 'instagram';
          return TabIcon({ ...props, name });
        },
        tabBarActiveBackgroundColor: 'pink', //터치시 배경
        tabBarActiveTintColor: 'black', //터치시 글자색
        tabBarInactiveTintColor: 'black', //비터치시 글자색
      })}
    >

결과물

 

버튼의 활성화 상태에 따라 다른 버튼을 렌더링하기

실제로 인스타그램 앱에서는 기본 탭 아이콘은 outline 아이콘으로 되어있고, 버튼이 활성화가 되면 내부가 채워진 아이콘으로 렌더링 된다. 

앞에서 "tabBarIcon에 설정된 함수에는 color, size, focused 값을 포함한 객체가 파라미터로 전달된다"라고 했다. 여기서 focused는 버튼의 선택된 상태를 나타내는 값으로, 이 값을 이용할 것이다.

 

코드를 수정하기에 앞서 버튼의 상태마다 다른 아이콘을 사용하려면 <Tab.Navigator>에서 전체적으로 아이콘을 설정하는 것보다 <Tab.Screen>에서 각각의 아이콘을 따로 설정하는 것이 더 편리하다. 위에 Tab Bar 버튼 아이콘 설정하기에서 첫번째 방식으로 코드를 변경한다. 

 

그리고 prop.focused를 사용해서 상황에 따라 각각 다른 아이콘을 렌더링한다.

 

<Tab.Navigator
      screenOptions={{
        tabBarActiveBackgroundColor: 'pink', //터치시 배경
        tabBarActiveTintColor: 'black', //터치시 글자색
        tabBarInactiveTintColor: 'black', //비터치시 글자색
      }}
    >
      <Tab.Screen
        name="Mainscreen"
        component={Mainscreen}
        options={{
          tabBarIcon: (props) =>
            TabIcon({
              ...props,
              name: props.focused ? 'home-variant' : 'home-variant-outline',
            }),
        }}
      />
 //아래는 생략

Mainscreen 터치시
Mypage 터치시
Instagram 터치시

 

Tab 완성~~~!