직딩 개발기-Navigation Bar

Lee young-jun
8 min readAug 6, 2023

--

일반적으로 앱에는 아래와 같이 휴대폰 시간, 배터리가 나타나는 부분 바로 아래 Navigation Bar라는게 있어요.

React Native에서 Navigation Bar대신 Navigation Header라고 부르지만 저는 iOS를 개발 했었기 때문에 Bar가 익숙해서 Bar로 불러요.

직딩 홈 화면의 Navigation Bar는 서서히 반투명해집니다. (그냥 보면 모르지만 아래로 스크롤하면 알 수 있어요 😋)

위와 같이 Navigation Option에는 Header를 설정하는 Prop이 많이 있지만 직딩의 Header는 직접 Component로 구현했어요.

먼저 위에서 언급한대로 Bar로 이름지어서 INavigationBar를 선언하고

export type INavigationBar = React.FC

NativeStackHeaderProps를 그대로 사용하는 대신 back, options는 제외하기 위해 Omit을 사용했어요.

export type Props = Omit<NativeStackHeaderProps, 'back' | 'options'> & {
logo?: boolean
}

export type INavigationBar = React.FC<Props>

Gradation

Gradation을 구현하기 위해 라이브러리를 설치하고 View 대신 사용해서

yarn add react-native-linear-gradient

navigationBar/index.ts

export const NavigationBar: INavigationBar = ({navigation}) => {
const theme = useTheme()

return (
<LinearGradient style={styles.container} colors={[theme.colors.background, Colors.transparent]}></LinearGradient>
)
}

navigationBar/styles.ts

export default StyleSheet.create({
container: {
height: 48,
},
})

실행해보면 뭔가 적용된게 확인되네요.

하지만 제가 원하는 건 홈 화면의 배경이 비추는 것을 원하는 것이기 때문에 헤더를 겹쳐지도록 설정했더니

export default StyleSheet.create({
container: {
position: 'absolute',
height: 48,
},
})

아예 보이질 않았어요.

높이는 지정되었지만 내부에 아무것도 없어서 넓이가 0인 상태인게 문제였죠. 그래서 넓이를 100%로 지정했더니

export default StyleSheet.create({
container: {
width: '100%',
position: 'absolute',
height: 48,
},
})

이제 제가 원하는대로 홈 화면의 배경이 비춰졌어요.

그런데 스크롤하지 않아도 홈 화면의 내용이 Navigation Bar 아래로 들어가는 문제가 생겼어요. 😭
이 문제를 해결 하기 위해 Navigation Bar 크기를 화면에서 참조할 수 있게 선언하고

helpers/common.ts

export const Sizes = {
navigationBarHeight: 48
}

상단 여백에 추가해줬어요.

screens/home/styles.ts

export default StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 40,
paddingTop: Sizes.navigationBarHeight + 24,
backgroundColor: 'yellow',
},
...
})

Logo

직딩을 실행하면 아래와 같이 로고가 나타나는데요.

Navigation Bar에도 로고를 넣어야 해서 Logo 컴포넌트를 만들기로 했어요.

Splash 화면과 다르게 Navigation Bar에는 작은 로고가 들어가야해서 LogoType을 선언하고

components/logo/types.ts

export type LogoType = 'small' | 'large'

type Props = StyleProps & {
type: LogoType
}

export type ILogo = React.FC<Props>

style도 따로 선언해서

components/logo/styles.ts

export default StyleSheet.create({
container: {
//
},
large: {
...Fonts.text.bold,
...fontSizeWithSpacing(40, 120),
},
small: {
...Fonts.text.bold,
...fontSizeWithSpacing(20, 150),
},
})

type에 따라 다른 크기의 로고를 나타나게 만든다음.

components/logo/index.tsx

export const Logo: ILogo = ({style, type}) => {
const theme = useTheme()
const isSmall = type === 'small'

return (
<View style={[styles.container, style]}>
<Text style={[isSmall ? styles.small : styles.large, {color: theme.colors.primary}]}>{'직딩'}</Text>
</View>
)
}

Navigation Bar에 적용했어요.

export const HomeScreen: IHomeScreen = ({navigation, route}) => {
...
useLayoutEffect(() => {
navigation.setOptions({
headerShown: true,
header: ({...props}) => <NavigationBar logo {...props} />,
})
})
...
}

완성된 화면이에요.

문제해결

helpers/font.ts

const lineHeightByLineSpacing = (fontSize: number, spacingPercent: number) => {
return fontSize * (spacingPercent / 100)
}

export const fontSizeWithSpacing = (size: number, spacingPercent: number) => ({
fontSize: size,
lineHeight: lineHeightByLineSpacing(size, spacingPercent),
})

참고자료

--

--

No responses yet