띠오니 개발자 성장일지
article thumbnail
반응형

1. Bottom Sheet

enter image description here

위 그림처럼, 아래에서 부터 위로 올라오는 일종의 보조 페이지를 Bottom Sheet 라고 한다.

 

1.1. react-native-bottom-sheet

내가 사용한 Bottom Sheet 라이브러리 이다.
최근까지도 업데이트가 이루어지고, 내가 원하는 형태의 UI라서 사용해야겠다고 생각했다.

gothom Bottom Sheet 의 Document 사이트 이다. 정리가 꽤 잘되어있는 것 같아 많아 참고했다.

 

1.2. 설치 및 설정 방법

Getting Start 를 따라 따라하면 된다.
처음에는 적당히 설치 CLI만 보고 설치했다가, 실행이 안되어서;; 뭐가 문제인지 몰랐는데 제대로 읽지도 않고 설치했던 게 문제였다.

 

1.2.1. 1. bottom sheet library 설치

<bash />
$ yarn add @gorhom/bottom-sheet@^4

1.2.2. 2. dependencies 추가

react-native-reanimated 와 react-native-gesture-handler 의존성을 추가해준다.

<code />
$ yarn add react-native-reanimated react-native-gesture-handler

1.2.3. 3. React Native Gesture Handler 추가 설정

Gesture Handler 공식 문서에 보면, 의존성 추가 후에 엔트리 포인트에 <GestureHandlerRootView> 라는 컴포넌트를 추가하라고 명시되어있다.

이 컴포넌트는 일반 View 처럼 작동하기 때문에, 화면을 채우려면 flex:1 속성도 지정해야 한다.

<bash />
$ yarn add react-native-gesture-handler

 

<javascript />
export default function App() { return ( <!-- 최상단 --> <GestureHandlerRootView style={{ flex: 1 }}> {/* content */} </GestureHandlerRootView> ); }

 

1.2.4. 4. React Native Reanimated v2 추가 설정

<bash />
$ yarn add react-native-reanimated

의존성 추가 후, babel.config.js 파일에도 설정을 추가해야 한다.

reanimated 플러그인은 plugins 목록들 중 마지막줄에 추가해야 한다.

<javascript />
// babel.config.js module.exports = { ... plugins: [ ... // plungins 중 마지막에 추가되어야 한다. 'react-native-reanimated/plugin', ], };

(error?)

플러그인 추가 후 "Reanimated 2 failed to create a worklet" error 에러가 날 수 있는데, 이는 대부분 앱 캐시를 삭제하면 해결된다.

<bash />
$ yarn start --reset-cache

 

1.2.5. 5. 변경내용 적용하기

<bash />
$ npx pod-install

 

 

1.3. 사용방법

나는 모달을 사용할 것이기 때문에, Bottom Sheet 공식 문서에 있는 예시코드를 복붙해왔다.

<javascript />
import React, { useCallback, useMemo, useRef } from 'react'; import { View, Text, StyleSheet, Button } from 'react-native'; import { BottomSheetModal, BottomSheetModalProvider, } from '@gorhom/bottom-sheet'; const App = () => { // ref const bottomSheetModalRef = useRef<BottomSheetModal>(null); // variables const snapPoints = useMemo(() => ['25%', '50%'], []); // callbacks const handlePresentModalPress = useCallback(() => { bottomSheetModalRef.current?.present(); }, []); const handleSheetChanges = useCallback((index: number) => { console.log('handleSheetChanges', index); }, []); // renders return ( <BottomSheetModalProvider> <View style={styles.container}> <Button onPress={handlePresentModalPress} title="Present Modal" color="black" /> <BottomSheetModal ref={bottomSheetModalRef} index={1} snapPoints={snapPoints} onChange={handleSheetChanges} > <View style={styles.contentContainer}> <Text>Awesome 🎉</Text> </View> </BottomSheetModal> </View> </BottomSheetModalProvider> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 24, justifyContent: 'center', backgroundColor: 'grey', }, contentContainer: { flex: 1, alignItems: 'center', }, }); export default App;

 

1.3.1. Android 터치 문제 발생

이렇게 복붙해오니 실행은 잘 되는데, Android 에서 모달창 스크롤이 안되는 것이었다.

구글링 해보니, 아까 의존성으로 추가했던 <GestureHandlerRootView> 이 렌더링 시 최상단으로 들어가 있어야 하는데, 예제 코드에서는 빠져있었다. <GestureHandlerRootView> 를 컴포넌트 최상단에다 넣어주자.

<javascript />
... // renders return ( <GestureHandlerRootView> <BottomSheetModalProvider> ... ... ... </BottomSheetModalProvider> </GestureHandlerRootView> ); ...

 

1.3.2. 뒷배경 누르면 모달창 닫기

이제 잘 움직이는 것 까지 확인했다.

뒷배경을 누르면 모달창이 사라지게 하는 코드는 아래와 같다. 추가해주자.

<javascript />
const renderBackdrop = useCallback( (props: any) => <BottomSheetBackdrop {...props} pressBehavior="close" />, [], ); ... <BottomSheetModal ref={bottomSheetModalRef} index={1} backdropComponent={renderBackdrop} // 추가 ... >

 

Bottom Sheet 의 공식문서 중 BottomSheetBackdrop 부분을 참고 하여 작성했는데 성공! 


2022.8.3 Backdrop 추가

 

<javascript />
// variables const snapPoints = useMemo(() => ['25%','65%'], []);

 

성공인줄 알았는데,, 바텀 시트가 멈추게 될 지점을 뜻하는 snap points가 25%, 65% 로 두군데가 설정되어있다 보니 중간에 멈추는 것이었다. 나는 바텀시트 65%로 한번 켜졌다가, 내리면 한번에 닫히길 원했다. 즉 내릴 때 멈추는 곳 없이 한번에 사라졌으면 했다.

근데 25% 로 걸리는 지점이 있어 스크롤해서 내릴 경우 다 닫히지 않는 것이다. 심지어 저 때는 Backdrop 먹히지도 않음. ㅋㅋ;

['0%', '65%'] 도 해봤는데 0값은 입력받을 수 없다며 오류가 났다. 최소 1% 부터 시작인 것 같다.

 

삽질결과!!!!! 진짜 찐으로 성공했다 ... ㅠ

1.3.2.1. 1. 우선 snapPoints는 원하는 지점으로 하나만 적어준다.

<javascript />
// variables const snapPoints = useMemo(() => ['65%'], []);

 

1.3.2.2. 2. BottomSheet 컴포넌트에서 인덱스를 0으로 수정해준다.

snapPoints 로 지정해준 배열의 첫번째(0번째 인덱스)값만큼 바텀시트 높이를 올리겠다는 의미이다.

<javascript />
<BottomSheetModal ref={bottomSheetModalRef} index={0} // snapPoint 배열의 값 (65%) snapPoints={snapPoints} backdropComponent={renderBackdrop} onChange={handleSheetChanges}> ...

 

1.3.2.3. 3. backdropComponent props 로 가서 pressBehavior="close" 로 되어있는지 확인한다.

<javascript />
const renderBackdrop = useCallback( props => ( <BottomSheetBackdrop {...props} pressBehavior="close" /> ), [], );

여기까지 하면, 뒷배경을 아무리 눌러도 꺼지지 않는 바텀시트가 완성된다. 짜잔 ~~ ^^ㅗ

이다음부터 해결하느라 엄청난 삽질 ...

 

1.3.2.4. 4. Backdrop 컴포넌트에 옵션을 추가한다!!

<javascript />
const renderBackdrop = useCallback( props => ( <BottomSheetBackdrop {...props} pressBehavior="close" appearsOnIndex={0} // 이거 추가 disappearsOnIndex={-1} // 이거 추가 /> ), [], );

나도 정확하게 이해한건 아니지만, 대충 이해한 걸로는 ...

bottomSheet의 index와, backdrop의 index가 별개로 있어서,

우리가 bottom sheet의 snap points 를 한개로 수정해버려 index값이 달라졌기 때문에,

bottom sheet와 backdrop의 index가 달라져서 나타나지 않은 것이다.

 

react native bottom sheet 문서 - BottomSheetBackdrop 속성값

 

예를 들자면,

처음에 snap points 배열이 2개(25%, 65%)일 때는 index가 0, 1 이 가능했다.

위 사진의 backdrop 속성을 보면, 뒷배경이 나타나는 appearsOnIndex 속성의 default가 1이다. 즉, snap points가 index[1] 을 가르키고 있을 때 backdrop이 나타나는 것이다.

그래서 65% 를 가르키고 있을 때 뒷배경이 나와 뒷배경 터치가 가능했고,

0번째 인덱스인 25% 지점으로 내리면, disappearsOnIndex 속성의 default값 때문에  뒷배경이 사라지는 것이었다. 

 

snap point를 하나 지웠을 때는 가능한 index가 0 뿐인데,

appearsOnIndex 는 default가 1이라서 1번째 인덱스일 때만 나타나기 때문에, 0번째 인덱스에서 놀고 있으니 아무리 삽질해도 나타나지 않는 것이었다 ㅠㅠ 

위에 코드 적어놓은 것 처럼 appearsOnIndex 를 0으로 바꾸고, disapearsOnIndex를 -1 로 (바텀시트를 아예 닫았을때 배경이 사라지게) 수정하면 된다!!

 

성공!!

 

 

도움이 되셨다면 좋아요나 댓글 부탁드립니다 :)!!!!!

반응형
profile

띠오니 개발자 성장일지

@띠오니

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!