티스토리 뷰

반응형

1. 이번 포스팅의 목적

Bottom-Tab Navigator가 메인화면에 있고,

어떤 Tab을 누르면(또는 어떤 Component를 누르면) 하단 탭 없이 화면을 전체 다 덮게 뜨게 하고 싶을 수 있다.

(예를 들어보자면, 쿠팡앱에서 하단 탭에 "검색"을 누를 때, 메인에서 상품 하나를 눌렀을 때 등)

 

이때, navigation을 어떤 식으로 구성하면 되는지 알아보자.

예제는
styled-component, 반응형디자인([React Native] 10. 반응형 디자인 적용하기) 을 사용했습니다.

 

 

2. Bottom Tab Navigator 구성

Bottom Tab Navigator는 이전 포스팅_[React-Native] 8. Bottom Tab Navigator 사용하기_의 예제를 사용한다.

이전 포스팅에서, 아래와 같은 화면을 만들었었다.

위 화면에서, "카테고리" 탭을 눌렀을 때 하단탭이 없는 새로운 화면을 띄워보자.

 

 

 

3. Stack Navigator 구성

 1) 우선, "카테고리" 탭을 눌렀을 때 나타날 화면으로 "NewScreen"이란 Component를 만들어준다.

    (styled-component 사용-Placeholder 정의)

const Placeholder = Styled.View`
  width: ${widthPercentage(360)}px;
  height: ${ScreenHeight + StatusHeight}px;
  paddingLeft: ${widthPercentage(155)}px;
  paddingTop: ${heightPercentage(360)}px;
  background-color: #FFFF11;
`;

const NewScreen = () => {
  return (
    <Placeholder style={{ backgroundColor: "#FF1111" }}>
      <Text>New Screen</Text>
    </Placeholder>
  );
};

 

 

 2) Stack Navigator를 정의한다.

import { createStackNavigator, StackNavigationProp } from "@react-navigation/stack";

const Stack = createStackNavigator();

const WholeStack = () => {
  return (
    <Stack.Navigator initialRouteName="TabNavi">
      <Stack.Screen
        name="TabNavi"
        component={TabNavi}
        options={{ headerShown: false, animationEnabled: false }}
      />
      <Stack.Screen
        name="NewScreen"
        component={NewScreen}
        options={{ headerShown: false, animationEnabled: false }}
      />
    </Stack.Navigator>
  );
};

export default () => {
  return (
    <NavigationContainer>
      <WholeStack />
    </NavigationContainer>
  );
};

첫 번째 Stack.Screen은 이전 포스팅에서 만든 Bottom Tab Navigator로,

두 번째 Stack.Screen은 위에서 만든 NewScreen(탭을 눌렀을 때 띄우고 싶은 화면)이란 component로 설정해준다.

 

→ 여기서 options 중 headerShown은 상단 Navigation header를 띄울 것인지, animationEnable은 화면을 전환할 때 애니메이션 효과를 보여줄 것인지를 각각 의미한다.

 

Stack.Navigator의 prop에 initialRouteName="TabNavi"로 지정해
제일 첫 화면엔 하단탭이 있는 화면이 먼저 나오게 해 준다.

 

 

 

 3) Bottom Tab Navigator에 navigation을 인자로 전달한다.

 이전 포스팅 예제에서 TabNavi의 인자로 navigation을 전달해준다. 이 navigation은 위에서 만들어준 Stack Navigator의 navigation prop이다.

const TabNavi = ({ navigation }) => {

 

 

 4) 이전 포스팅 예제에서, "카테고리" 탭에 해당하는 Tab.Screen을 아래처럼 수정해준다.

 ...(생략)...
     <Tab.Screen
        name="Category"
        component={TabPlaceholder}
        options={{
          tabBarLabel: "카테고리",
          tabBarIcon: () => (
            <CategoryIcon width={widthPercentage(24)} height={heightPercentage(24)} />
          ),
        }}
        listeners={() => ({
          tabPress: (e) => {
            e.preventDefault();
            navigation.navigate("NewScreen");
          },
        })}
      />
 ...(생략)...

React Navigation에서 Screen에 대한 prop으로 listeners를 사용할 수 있다. (공식 문서)

listeners는 key값으로 이벤트 이름을, value값으로 listener callback을 정의해준다.

 

위의 예제에선 탭을 눌렀을 때 일어나는 이벤트인 tabPress를 key값으로 정의해주었다.

callback함수 내용으론,

  • e.preventDefault() : default action을 막음
  • navigation.navigate("NewScreen"): "NewScreen"이란 이름을 가진 Screen으로 이동

여기서 navigation은 3) 번에서 인자로 전달해준 것이다!

 

 

4. 마무리

자, 위의 코드들이 제대로 적용이 완료됐다면

아래 화면처럼 "카테고리" 탭을 눌렀을 때 새로운 화면에 잘 나오는 것을 확인할 수 있다!

 

 

 

> 전체 코드 보기

더보기

예제를 테스트하기 전 확인 사항

  • "./Config/ResponsiveSize": 화면 크기에 따른 component size 비율 조정 함수 정의 ([React Native] 10. 반응형 디자인 적용하기 참고)
  • Tab.Screen에 사용된 icon들은 수정해서 사용 (아래 예제에서 사용한 icon은 첨부하지 않겠습니다)
  • font 설정 필요 (or 지우고 테스트)
import React from "react";
import { Dimensions, StatusBar } from "react-native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { createStackNavigator, StackNavigationProp } from "@react-navigation/stack";
import { widthPercentage, heightPercentage, fontPercentage } from "./Config/ResponsiveSize";
import { NavigationContainer } from "@react-navigation/native";
import Styled from "styled-components/native";

const ScreenHeight = Dimensions.get("window").height;
let StatusHeight = StatusBar.currentHeight;
if (!StatusHeight) {
  StatusHeight = 0;
}

const Placeholder = Styled.View`
  width: ${widthPercentage(360)}px;
  height: ${ScreenHeight + StatusHeight}px;
  paddingLeft: ${widthPercentage(155)}px;
  paddingTop: ${heightPercentage(360)}px;
  background-color: #FFFF11;
`;

const Tab = createBottomTabNavigator();

const TabPlaceholder = () => {
  return (
    <Placeholder>
      <Text>example</Text>
    </Placeholder>
  );
};

const NewScreen = () => {
  return (
    <Placeholder style={{ backgroundColor: "#FF1111" }}>
      <Text>New Screen</Text>
    </Placeholder>
  );
};

const TabNavi = ({ navigation }) => {
  return (
    <Tab.Navigator
      tabBarOptions={{
        activeTintColor: "#7DD421",
        inactiveTintColor: "#222222",
        style: {
          height: heightPercentage(56),
          borderTopWidth: heightPercentage(0.5),
          borderTopColor: "#E9E9E9",
          backgroundColor: "#FFFFFF",
        },
        iconStyle: {
          marginTop: heightPercentage(10),
        },
        labelStyle: {
          fontFamily: "NotoSansKR-Regular",
          fontSize: fontPercentage(10),
        },
      }}
    >
      <Tab.Screen
        name="Home"
        component={TabPlaceholder}
        options={{
          tabBarLabel: "홈",
          tabBarIcon: ({ focused }) =>
            focused ? (
              <ActiveHomeIcon width={widthPercentage(24)} height={heightPercentage(24)} />
            ) : (
              <InactiveHomeIcon width={widthPercentage(24)} height={heightPercentage(24)} />
            ),
        }}
      />

      <Tab.Screen
        name="Category"
        component={TabPlaceholder}
        options={{
          tabBarLabel: "카테고리",
          tabBarIcon: () => (
            <CategoryIcon width={widthPercentage(24)} height={heightPercentage(24)} />
          ),
        }}
        listeners={() => ({
          tabPress: (e) => {
            e.preventDefault();
            navigation.navigate("NewScreen");
          },
        })}
      />

      <Tab.Screen
        name="Chatting"
        component={TabPlaceholder}
        options={{
          tabBarLabel: "채팅",
          tabBarIcon: ({ focused }) =>
            focused ? (
              <ActiveChatIcon width={widthPercentage(24)} height={heightPercentage(24)} />
            ) : (
              <InactiveChatIcon width={widthPercentage(24)} height={heightPercentage(24)} />
            ),
        }}
      />

      <Tab.Screen
        name="Profile"
        component={TabPlaceholder}
        options={{
          tabBarLabel: "프로필",
          tabBarIcon: ({ focused }) =>
            focused ? (
              <ActiveProfileIcon width={widthPercentage(24)} height={heightPercentage(24)} />
            ) : (
              <InactiveProfileIcon width={widthPercentage(24)} height={heightPercentage(24)} />
            ),
        }}
      />
    </Tab.Navigator>
  );
};

const Stack = createStackNavigator();

const WholeStack = () => {
  return (
    <Stack.Navigator initialRouteName="TabNavi">
      <Stack.Screen
        name="TabNavi"
        component={TabNavi}
        options={{ headerShown: false, animationEnabled: false }}
      />
      <Stack.Screen
        name="NewScreen"
        component={NewScreen}
        options={{ headerShown: false, animationEnabled: false }}
      />
    </Stack.Navigator>
  );
};

export default () => {
  return (
    <NavigationContainer>
      <WholeStack />
    </NavigationContainer>
  );
};

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함