TabBar
하단 탭바의 면. 네비게이션은 안 든다.
import { TabBar } from '@coldsurfers/surf-ui/native/TabBar'import { Text } from '@coldsurfers/surf-ui/native/Text'import { House, Search, Tickets, UserRound } from 'lucide-react'import { useState } from 'react'import { View } from 'react-native'const TABS = [ { key: 'feed', label: '피드', Icon: House }, { key: 'events', label: '이벤트', Icon: Tickets }, { key: 'search', label: '검색', Icon: Search }, { key: 'my', label: '나의 창꼬', Icon: UserRound },]export default function Example() { const [selected, setSelected] = useState('feed') return ( <> <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text textStyle="body" tone="muted"> {TABS.find((tab) => tab.key === selected)?.label} </Text> </View> <TabBar> {TABS.map(({ key, label, Icon }) => ( <TabBar.Item key={key} label={label} active={key === selected} onPress={() => setSelected(key)} renderIcon={({ color, size, strokeWidth }) => ( <Icon color={color} size={size} strokeWidth={strokeWidth} aria-hidden /> )} /> ))} </TabBar> </> )}웹에 짝이 없는 표면입니다. 이건 면(chrome)이지 네비게이션도 배치도 아닙니다.
무엇을 들고 무엇을 안 드는가
| 여기 | 여기가 아닌 곳 |
|---|---|
| 색 · 테두리 · 높이(safe-area 포함) · 아이템 배치 · 라벨 서식 · 활성 tint | 라우터 배선 · route → 아이콘 매핑 · 햅틱 → 소비처 |
화면 하단 고정 · 숨김 애니메이션 → AnimatedTabBar |
위치를 정하지 않는 이유는 의존입니다. reanimated 애니메이션 스타일은 Animated.* 에만
먹으므로, 위치와 이동을 한 컴포넌트가 들면 이 파일이 reanimated 를 물게 됩니다. 그러면
애니메이션을 안 쓰는 소비처까지 그 의존을 집니다. 여기 의존은 emotion 과
safe-area-context 둘뿐입니다.
아이콘을 노드가 아니라 함수로 받는 이유
색 · 크기 · 굵기는 활성 여부에서 나오는 이 면의 계약이고, 무엇을 그릴지는 소비처의 것입니다. 노드로 받으면 소비처가 tint 를 알아야 하고, 그러면 색 계약이 밖으로 샙니다.
renderIcon={({ color, size, strokeWidth }) => (
<House color={color} size={size} strokeWidth={strokeWidth} />
)}활성 상태는 채움 아이콘 세트가 아니라 굵기와 accent 색으로 말합니다 (1.5 → 2.5).
높이는 useTabBarHeight() 에서 읽는다
AnimatedTabBar 로 띄우면 탭바가 레이아웃에서 빠져 자리를 비워 주지 않습니다. 화면이
자기 콘텐츠 아래 여백을 이 값으로 잡아야 합니다.
const tabBarHeight = useTabBarHeight()
<ScrollView contentContainerStyle={{ paddingBottom: tabBarHeight }} />인셋을 상수로 박지 않습니다 — 홈 인디케이터 높이는 기기마다 다르고 0 인 기기(SE · iPad)도 있어서, 박으면 그런 기기에서 탭바 아래가 빈 채로 남습니다.
화면 전체가 이 여백을 져야 하면 직접 계산하는 대신
AppScreen 의 offsetBottom="tabBar" 를 씁니다.
위 미리보기의 높이는 실기기 높이가 아닙니다
브라우저에서는 Platform.OS === 'web' 이라 아래 여백이 0 입니다. 실기기에서는 iOS 가
홈 인디케이터 인셋만큼, Android 가 12 만큼 더 큽니다.
그리고 useSafeAreaInsets() 를 부르므로 앱 루트에 SafeAreaProvider 가 있어야 합니다.
Props
Prop
Type
그 밖의 ViewProps 속성은 루트 엘리먼트로 그대로 통과한다.
TabBar.Item
Prop
Type
그 밖의 TouchableOpacityProps 속성은 루트 엘리먼트로 그대로 통과한다.