안드로이드의 경우 네이티브 NestedScrollView를 사용하고, iOS는 네이티브 스크롤뷰를 조작하여 내부 스크롤 뷰와 함께 작동할 수 있게 만든 스크롤뷰입니다.
안드로이드
리액트 네이티브의 ScrollView 컴포넌트는 네이티브 ScrollView 뷰를 제어하는 컴포넌트입니다.
기본 ScrollView는 중첩되면 내부 ScrollView가 스크롤 이벤트를 먼저 소비하기 때문에, 바깥 ScrollView가 스크롤을 인식하지 못합니다. (참고: [Android] NestedScrollView에 대해 알아보자!)
반면 NestedScrollView는 onNestedPreScroll, onNestedPreFling 메서드를 오버라이드하여 자식 스크롤뷰의 스크롤, Fling(손가락 튕기는 제스쳐) 이벤트를 가로챌 수 있습니다.
nested-scroll은 NestedScrollView를 제어하는 컴포넌트입니다.
움직인 y(dy)에서 자식의 스크롤값(comsumed[1])을 뺀 값이 0보다 크다는 것은 자식은 맨 위로 올라갔고, 그만큼 자신의 스크롤을 올립니다.
public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
super.onNestedPreScroll(target, dx, dy, consumed, type);
int dyUnconsumed = dy - consumed[1]; // 요기 !!
if (dyUnconsumed > 0) { // 요기 !!
final int oldScrollY = getScrollY();
scrollBy(0, dyUnconsumed); // 요기 !!
final int myConsumed = getScrollY() - oldScrollY;
consumed[1] += myConsumed;
}
}
iOS
제스쳐가 인식되면 gestureRecognizer가 호출됩니다.
매개변수인 touch 객체의 view에 터치된 위치의 뷰가 들어 있습니다.
부모 뷰로 올라가면서 스크롤 가능한 뷰(내부 스크롤뷰)를 찾습니다.
스크롤 가능한 뷰라면 자신을 contentOffset 속성에 대한 observer로 등록합니다.
내부 스크롤뷰의 contentOffset이 변경되면 observeValueForKeyPath 메서드가 호출됩니다.
gestureRecognizer에서 YES를 반환하면 내부 스크롤뷰를 스크롤해도 자신도 스크롤되면서 scrollViewDidScroll 메서드가 호출됩니다.
observeValueForKeyPath 메서드와 scrollViewDidScroll 메서드에서 자신과 내부 스크롤뷰의 위치를 조정합니다.
이 부분만 보겠습니다.
CGFloat dy = old - new;
if (dy < 0) {
if (lt(main.contentOffset.y, self.headerScrollRange) && target.contentOffset.y > 0) {
_nextReturn = true;
target.contentOffset = CGPointMake(0, fmax(0, old));
return;
}
// ...
}
dy가 0보다 작다는 것은 스크롤을 아래로 내린 것입니다.
이때 두 스크롤뷰가 아래로 내려가는데, 외부 스크롤뷰가 헤더 범위까지 먼저 내려가야 하므로, 내부 스크롤뷰의 위치를 원래대로 돌려서 위치를 고정시킵니다.
제약사항으로는 NestedScrollViewHeader를 제외한 한 개의 자식만 랜더링할 수 있습니다.
'리액트 네이티브' 카테고리의 다른 글
리액트 네이티브에서 IntersectionObserver를 대체하는 방법 (0) | 2024.10.16 |
---|---|
리액트 네이티브 WebView 중첩 (0) | 2024.10.03 |
리액트 네이티브가 느린 이유 (0) | 2024.08.27 |
리액트 네이티브 터치 이벤트 흐름 (0) | 2024.08.27 |
리액트 네이티브에서 JS와 Java가 통신하는 방법 (0) | 2024.08.27 |