React Native에서 ScrollView의 현재 스크롤 위치 가져 오기


115

<ScrollView>React Native에서 현재 스크롤 위치 또는 구성 요소 의 현재 페이지를 가져올 수 있습니까?

그래서 다음과 같습니다.

<ScrollView
  horizontal={true}
  pagingEnabled={true}
  onScrollAnimationEnd={() => { 
      // get this scrollview's current page or x/y scroll position
  }}>
  this.state.data.map(function(e, i) { 
      <ImageCt key={i}></ImageCt> 
  })
</ScrollView>

관련성 : 이 정보를 얻는 편리한 방법 추가를 제안 하는 GitHub 문제 .
Mark Amery dec.

답변:


208

시험.

<ScrollView onScroll={this.handleScroll} />

그리고:

handleScroll: function(event: Object) {
 console.log(event.nativeEvent.contentOffset.y);
},

5
스크롤 값이 변경 될 때 항상 실행되는 것은 아닙니다. 예를 들어 scrollview의 크기가 조정되어 이제 화면에 모든 것을 한 번에 표시 할 수있는 경우 항상 onScroll 이벤트가 표시되는 것은 아닙니다.
Thomas Parslow 2015-08-12

19
또는 event.nativeEvent.contentOffset.x수평 ScrollView가있는 경우;) 감사합니다!
Tieme 16.05.05

2
이것은 또한 우리는 Android에서 사용할 수 있습니다.
Janaka Pushpakumara

4
안타깝게도 scrollEventThrottle(매 프레임마다 이벤트를 얻기 위해) 16 이하로 설정하지 않으면 최종 프레임 에서 오프셋이보고된다는 보장이 없습니다. 즉, 스크롤이 완료된 후 올바른 오프셋이 있는지 확인해야합니다. scrollEventThrottle={16}성능에 미치는 영향 을 설정 하고 수용 해야 합니다.
Mark Amery

@bradoyler :의 최대 값을 아는 것이 가능 event.nativeEvent.contentOffset.y합니까?
Isaac

55

면책 조항 : 다음은 주로 React Native 0.50에서 직접 실험 한 결과입니다. ScrollView문서는 현재 아래에 적용되는 많은 정보가 누락; 예를 들어onScrollEndDrag 완전히 문서화되지 않았습니다. 여기에있는 모든 것은 문서화되지 않은 행동에 의존하기 때문에 안타깝게도이 정보가 지금부터 1 년 또는 심지어 한 달 후에도 정확할 것이라고 약속 할 수 없습니다.

또한 아래의 모든 것은 y 오프셋에 관심이 있는 순전히 수직 스크롤 뷰를 가정합니다 . 필요한 경우 x 오프셋으로 변환하는 것은 독자에게 쉬운 연습이되기를 바랍니다.


ScrollView테이크 에 대한 다양한 이벤트 핸들러를 사용하면 을 event통해 현재 스크롤 위치를 가져올 수 있습니다 event.nativeEvent.contentOffset.y. 이러한 핸들러 중 일부는 아래에 설명 된대로 Android와 iOS간에 약간 다른 동작을합니다.

onScroll

Android에서

사용자가 스크롤하는 동안 모든 프레임, 사용자가 놓은 후 스크롤 뷰가 글라이딩하는 동안 모든 프레임, 스크롤 뷰가 정지 될 때 마지막 프레임에서, 그리고 프레임의 결과로 스크롤 뷰의 오프셋이 변경 될 때마다 발생합니다. 변경 (예 : 가로에서 세로로의 회전으로 인해).

iOS에서

화재는 사용자가 드래그하거나 스크롤보기 글라이딩 동안 몇몇 주파수에 의해 결정되는 반면 scrollEventThrottle때 프레임 당 많아야 한 번 scrollEventThrottle={16}. 경우 사용자가 스크롤 뷰를 해제 해 글라이드에 충분한 힘을 가지고 동안 onScroll핸들러는 화재 때 글라이딩 후 휴식을 제공합니다. 사용자가 드래그 한 후이 정지 상태에서 스크롤 뷰를 해제하는 경우에는, onScroll되어 있지 않는 한 최종 위치에 대한 화재 보장 scrollEventThrottle같은 설정되었는지 onScroll화재 스크롤의 모든 프레임.

scrollEventThrottle={16}더 큰 숫자로 설정하여 줄일 수있는 설정 성능 비용 이 있습니다. 그러나 이것은 onScroll모든 프레임을 발사하지는 않는다는 것을 의미합니다 .

onMomentumScrollEnd

글라이딩 후 스크롤 뷰가 멈출 때 발생합니다. 사용자가 움직이지 않도록 고정되어있는 동안 스크롤 뷰를 놓으면 전혀 실행되지 않습니다.

onScrollEndDrag

사용자가 스크롤보기가 정지 된 상태로 있거나 글라이딩을 시작하는지 여부에 관계없이 스크롤보기 드래그를 중지하면 실행됩니다.


이러한 동작 차이를 고려할 때 오프셋을 추적하는 가장 좋은 방법은 정확한 상황에 따라 다릅니다. 가장 복잡한 경우 ( ScrollView회전으로 인한의 프레임 변경 처리를 포함하여 Android 및 iOS를 지원해야하며 Android에서 설정 scrollEventThrottle에서 16으로 의 성능 저하를 받아들이고 싶지 않음 ) 처리해야합니다. 스크롤 뷰 의 내용도 변경되면 엉망입니다.

가장 간단한 경우는 Android 만 처리해야하는 경우입니다. 그냥 사용하십시오 onScroll:

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
>

또한 아이폰 OS를 지원하기 위해, 만약 당신이하고있는 화재 행복 onScroll핸들러마다 프레임을 그의 성능에 영향을 동의 한 경우에 당신이 핸들 프레임 변경이 필요하지 않습니다, 그것은 조금 더 복잡 비트 전용입니다 :

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  scrollEventThrottle={16}
>

iOS에서 성능 오버 헤드를 줄이면서 스크롤 뷰가 고정되는 위치를 기록하도록 보장 scrollEventThrottle하기 위해 onScrollEndDrag핸들러를 늘리고 추가로 제공 할 수 있습니다 .

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  onScrollEndDrag={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  scrollEventThrottle={160}
>

우리가 (우리가 스크롤 뷰의 프레임에 사용 가능한 높이를 변경, 장치가 회전 할 수 있기 때문에 예를) 및 / 또는 내용 변경 프레임 변경을 처리하는 경우에, 우리는 또한 모두를 구현해야 onContentSizeChange하고 onLayout모두의 높이를 추적하는 스크롤 뷰의 프레임과 그 내용을 확인하여 가능한 최대 오프셋 을 지속적으로 계산하고 프레임 또는 내용 크기 변경으로 인해 오프셋이 자동으로 감소한시기를 추론합니다.

<ScrollView
  onLayout={event => {
    this.frameHeight = event.nativeEvent.layout.height;
    const maxOffset = this.contentHeight - this.frameHeight;
    if (maxOffset < this.yOffset) {
      this.yOffset = maxOffset;
    }
  }}
  onContentSizeChange={(contentWidth, contentHeight) => {
    this.contentHeight = contentHeight;
    const maxOffset = this.contentHeight - this.frameHeight;
    if (maxOffset < this.yOffset) {
      this.yOffset = maxOffset;
    }
  }}
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y;
  }}
  onScrollEndDrag={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y;
  }}
  scrollEventThrottle={160}
>

네, 꽤 끔찍합니다. 또한 스크롤 뷰의 프레임과 내용의 크기 를 동시에 변경하는 경우 항상 올바르게 작동 할 것이라고 100 % 확신하지는 않습니다 . 하지만 이것이 제가 생각할 수있는 최선의 방법이며, 이 기능이 프레임 워크 자체에 추가 될 때까지 누구나 할 수있는 최선이라고 생각합니다.


19

Brad Oyler의 대답이 맞습니다. 단 하나의 이벤트 만 받게됩니다. 스크롤 위치를 지속적으로 업데이트해야하는 경우 다음 scrollEventThrottle과 같이 소품을 설정해야합니다 .

<ScrollView onScroll={this.handleScroll} scrollEventThrottle={16} >
  <Text>
    Be like water my friend 
  </Text>
</ScrollView>

그리고 이벤트 핸들러 :

handleScroll: function(event: Object) {
  console.log(event.nativeEvent.contentOffset.y);
},

성능 문제가 발생할 수 있습니다. 그에 따라 스로틀을 조정하십시오. 16은 가장 많은 업데이트를 제공합니다. 0 하나만.


12
이것은 올바르지 않습니다. 우선 scrollEvenThrottle은 iOS에서만 작동하며 Android에서는 작동하지 않습니다. 둘째, onScroll 호출을받는 빈도 만 규제합니다. 기본값은 하나의 이벤트가 아니라 프레임 당 한 번입니다. 1은 더 많은 업데이트를 제공하고 16은 덜 제공합니다. 0이 기본값입니다. 이 대답은 완전히 틀 렸습니다. facebook.github.io/react-native/docs/...
Teodors

1
Android가 React Native에서 지원되기 전에 대답 했으므로 대답은 iOS에만 적용됩니다. 기본값은 0이며보기가 스크롤 될 때마다 한 번만 스크롤 이벤트가 전송됩니다.
cutemachine dec

1
function (event : Object) {}에 대한 es6 표기법은 무엇입니까?
cbartondock

1
그냥 function(event) { }.
cutemachine

1
@Teodors이 답변은 Android를 다루지 않지만 나머지 비판은 완전히 혼란 스럽습니다. "적은"업데이트를 제공 scrollEventThrottle={16}하지 않습니다 . 1-16 범위의 숫자는 가능한 최대 업데이트 속도를 제공합니다. 기본값 0은 사용자가 스크롤을 시작할 때 하나의 이벤트 (iOS에서)를 제공 한 다음 후속 업데이트가 없습니다 (매우 쓸모없고 버그 일 수 있음). 기본값은 "프레임 당 한 번" 이 아닙니다 . 귀하의 의견에있는이 두 가지 오류 외에도 귀하의 다른 주장은 답변이 말하는 것과 모순되지 않습니다 (그렇다고 생각하는 것 같지만).
마크 아메리

7

사용자가 스크롤 할 때마다 추적하지 않고 단순히 현재 위치 (예 : 일부 버튼을 눌렀을 때)를 가져 오려면 onScroll리스너 를 호출 하면 성능 문제가 발생합니다. 현재 단순히 현재 스크롤 위치를 얻는 가장 효과적인 방법은 react-native-invoke 패키지를 사용하는 것입니다. 이 정확한 것에 대한 예가 있지만 패키지는 다른 여러 가지 작업을 수행합니다.

여기에서 그것에 대해 읽어보십시오. https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd


2
오프셋을 요청하는 더 깔끔한 방법 (좀 덜 해키)이 현재 존재하는지 알고 있습니까? 아니면 이것이 당신이 알고있는 가장 좋은 방법입니까? (나는 외모가 이해 할 수있는 유일한 일이기 때문에이 대답이 upvoted되지 않는 이유 궁금)
cglacet

1
패키지가 더 이상 존재하지 않습니다. 어딘가로 이동 했습니까? Github에서 404를 표시합니다.
Noitidart

6

원래 질문이 요청하면서 스크롤이 끝난 후 x / y를 얻으려면 가장 쉬운 방법은 다음과 같습니다.

<ScrollView
   horizontal={true}
   pagingEnabled={true}
   onMomentumScrollEnd={(event) => { 
      // scroll animation ended
      console.log(e.nativeEvent.contentOffset.x);
      console.log(e.nativeEvent.contentOffset.y);
   }}>
   ...content
</ScrollView>

-1; onMomentumScrollEnd스크롤 뷰가 사용자가 놓을 때 약간의 추진력이있는 경우에만 트리거됩니다. 스크롤 뷰가 움직이지 않는 동안 놓으면 모멘텀 이벤트가 발생하지 않습니다.
Mark Amery dec.

1
나는 onMomentumScrollEnd를 테스트했고 그것을 트리거하지 않는 것이 불가능했고, 다른 방법으로 스크롤을 시도했고, 스크롤이 최종 위치에있을 때 터치를 놓아 보았고 또한 트리거되었습니다. 그래서이 대답은 내가 테스트 할 수있는 한 정확합니다.
fermmm

6

위의 답변은 다른 API를 사용하여 위치를 얻는 방법을 알려, onScroll, onMomentumScrollEnd등; 페이지 인덱스를 알고 싶다면 오프셋 값을 사용하여 계산할 수 있습니다.

 <ScrollView 
    pagingEnabled={true}
    onMomentumScrollEnd={this._onMomentumScrollEnd}>
    {pages}
 </ScrollView> 

  _onMomentumScrollEnd = ({ nativeEvent }: any) => {
   // the current offset, {x: number, y: number} 
   const position = nativeEvent.contentOffset; 
   // page index 
   const index = Math.round(nativeEvent.contentOffset.x / PAGE_WIDTH);

   if (index !== this.state.currentIndex) {
     // onPageDidChanged
   }
 };

여기에 이미지 설명 입력

iOS에서 ScrollView와 가시 영역 간의 관계는 다음과 같습니다. 사진은 https://www.objc.io/issues/3-views/scroll-view/에서 가져온 것입니다.

참조 : https://www.objc.io/issues/3-views/scroll-view/


현재 항목의 "색인"을 얻으려면 이것이 정답입니다. event.nativeEvent.contentOffset.x / deviceWidth를 사용합니다. 당신의 도움을 주셔서 감사합니다!
Sara Inés Calderón

1

이것이 뷰에서 현재 스크롤 위치를 실시간으로 얻는 방법입니다. 내가하는 일은 스크롤 이벤트 리스너와 scrollEventThrottle을 사용하는 것뿐입니다. 그런 다음 내가 만든 스크롤 기능을 처리하고 소품을 업데이트하는 이벤트로 전달합니다.

 export default class Anim extends React.Component {
          constructor(props) {
             super(props);
             this.state = {
               yPos: 0,
             };
           }

        handleScroll(event){
            this.setState({
              yPos : event.nativeEvent.contentOffset.y,
            })
        }

        render() {
            return (
          <ScrollView onScroll={this.handleScroll.bind(this)} scrollEventThrottle={16} />
    )
    }
    }

성능을 고려하여 onScroll에서 상태를 업데이트하는 것이 좋습니까?
Hrishi

1

onScroll을 사용하면 무한 루프가 시작됩니다. onMomentumScrollEnd 또는 onScrollEndDrag를 대신 사용할 수 있습니다.


0

페이지의 경우 기본적으로 위의 방법을 사용하여 정확하게 수행하는 고차 구성 요소를 작업 중입니다. 초기 레이아웃 및 콘텐츠 변경과 같은 미묘한 부분에 도달하면 실제로 약간의 시간이 걸립니다. 나는 그것을 '올바르게'했다고 주장하지는 않지만 어떤 의미에서는 이것을 신중하고 일관되게 수행하는 구성 요소를 사용하는 정답을 고려할 것입니다.

참조 : react-native-paged-scroll-view . 내가 모든 일을 잘못 했더라도 피드백을 좋아할 것입니다!


1
이것은 굉장합니다. 만들어 주셔서 감사합니다. 나는 이것이 즉시 매우 유용하다는 것을 알았습니다.
Marty Cortez

너무 기뻐요! 피드백에 감사드립니다!

1
사과 -1. 프로젝트가 유지 관리되지 않고 구성 요소에 "몇 가지 못생긴 버그" 가 있음을 공개적으로 인정하기 때문입니다 .
Mark Amery dec.

@MarkAmery 피드백에 감사드립니다. :) 오픈 소스의 시간을 찾는 것은 쉽지 않습니다.

-3

contentOffset왼쪽 상단 스크롤 오프셋을 포함하는 개체를 제공 할 것이라고 생각 합니다.

http://facebook.github.io/react-native/docs/scrollview.html#contentoffset


4
이것은 게터가 아니라 세터가 아닌가?
아론 왕

1
-1 이건 말도 안됩니다. 링크 한 문서는 JavaScript 속성이 아닌 JSX prop에 대한 것이며 오프셋 을 설정 하는 데만 사용할 수 있으며 오프셋을 가져 오는 데 사용할 수 없습니다.
Mark Amery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.