React Native에서 사용되는 바텀 시트의 높이를 고정해 보자
못말리는 짱구도 아니고 못말리는 바텀 시트
세 줄 요약
@gorhom/bottom-sheet
iOS, Android에서 bottom-sheet의 UI가 동일하게 구성되지 않음- 높이가 동적으로 적용되어 있기 때문에 발생한 문제였음!
enableDynamicSizing
을false
로 설정하기!
1. 개요
바텀 시트를 구현하기 위해 @gorhom/bottom-sheet
라이브러리를 사용하고 있는데 하단에 고정해 둔 버튼이 예상대로 렌더링 되지 않는 문제가 발생하였다.
2. 문제 원인
동일한 높이의 snapPoints
를 설정해 두고 렌더링 한 결과 위 이미지와 같이 iOS에서는 예상대로 버튼이 렌더링 되지만 Android에서는 예상과는 다르게 렌더링 되는 문제가 발생하였다.
포스팅을 다 한 시점에서 알게 되었는데 iOS에서도 10번 중의 1번 정도 위 문제가 발생하고 있었다.
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
32
33
34
35
36
37
38
function BottomSheet() {
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
const snapPoints = useMemo(() => ['50%'], []);
const handlePresentModalPress = () => bottomSheetModalRef.current?.present();
return (
<View style={styles.container}>
<Pressable style={styles.presentButton} onPress={handlePresentModalPress}>
<Text style={styles.presentText}>Present Modal</Text>
</Pressable>
<BottomSheetModal
ref={bottomSheetModalRef}
index={1}
snapPoints={snapPoints}
>
<BottomSheetView style={styles.contentContainer}>
<Text style={styles.text}>Awesome 🎉</Text>
<Pressable style={styles.buttonContainer}>
<View style={styles.button}>
<Text style={styles.buttonLabel}>버튼</Text>
</View>
</Pressable>
</BottomSheetView>
</BottomSheetModal>
</View>
);
}
const styles = StyleSheet.create({
// ...
buttonContainer: {
position: 'absolute',
bottom: 44
}
})
코드 자체에서 문제가 있는 것인지 확인해 보았지만, Android 기기에 따라 다르게 렌더링 되도록 작성한 코드가 없었기에 코드상에 문제는 없다고 생각했다. 그렇다면 @gorhom/bottom-sheet
에서 제공해 주는 바텀 시트가 동적으로 위치를 설정할 수 있기 때문에 그런 것은 아닐까하여 확인해 본 결과 아래 이미지와 같이 버튼이 하단에 정상적으로 렌더링 되고 있는 것을 확인할 수 있었다.
2-1. snapPoints 분석
잠깐 snapPoints
의 의미부터 짚고 넘어가면 좋을 것 같다. React Native Bottom Sheet에서 snapPoinst
속성을 다음과 같이 설명하고 있다.
snapPoints
: 바텀 시트가 스냅 될 지점입니다. 지점은 아래에서 위로 정렬되어야 합니다. 숫자, 문자열 또는 혼합 배열을 허용합니다.
바텀 시트가 스냅 될 지점이라는 것은 풀어서 말하면 바텀 시트가 특정 높이로 스냅(고정) 될 위치를 의미한다. 그렇다면 snapPoints
를 하나의 값만으로 지정한 상황에서는 반드시 하나의 고정 위치만을 가져야 할 텐데 바텀 시트가 렌더링 될 때 Android에서는 snapPoints
에 100%가 삽입되고 있기 때문에 100%로도 스크롤링을 할 수 있는 게 아닐까?
1
2
3
4
5
// iOS
const snapPoints = useMemo(() => ['50%'], []);
// Android
const snapPoints = useMemo(() => ['50%', '100%'], []); // 🧐 Android에서는 렌더링 시점에서 100% 삽입?
snapPoints
props를 다루고 있는 BottomSheet.tsx 컴포넌트를 확인해 본 결과 기기 별로 다르게 처리하고 있는 것으로 보이진 않는다.
그렇다면 snapPoints
의 문제는 아닌 것 같다.
2-2. Dynamic Resizing Off
1
const DEFAULT_DYNAMIC_SIZING = true;
BottomSheet Constants에서 위와 같이 DEFAULT_DYNAMIC_SIZING
변수의 Default 값이 true
로 설정된 것을 확인할 수 있는데 이로 인해 자동으로 높이 조절이 되고 Android에서는 100%
까지 스크롤링이 되게 된 것 같다.
그렇다면 snapPoints
를 설정해 주고 자동으로 높이 조절이 불가능하게 하면 되지 않을까? 이 아이디어를 가지고 snapPoints
를 설정해 주고 enableDynamicSizing
props를 false
로 설정하였다.
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
function BottomSheet() {
// ...
return (
<View style={styles.container}>
<Pressable style={styles.presentButton} onPress={handlePresentModalPress}>
<Text style={styles.presentText}>Present Modal</Text>
</Pressable>
<BottomSheetModal
ref={bottomSheetModalRef}
index={1}
snapPoints={snapPoints}
enableDynamicSizing={false} // ✅ 동적 높이 조절 기능 Off
>
<BottomSheetView style={styles.contentContainer}>
<Text style={styles.text}>Awesome 🎉</Text>
<Pressable style={styles.buttonContainer}>
<View style={styles.button}>
<Text style={styles.buttonLabel}>버튼</Text>
</View>
</Pressable>
</BottomSheetView>
</BottomSheetModal>
</View>
);
}
그 결과 짜잔 🎉
정상적으로 내가 예상하는 바텀 시트가 렌더링이 되는 모습을 확인할 수 있다.