Gesture Responder System

젠젠·2022년 2월 21일
0

reactnative

목록 보기
2/2

ScrollView에 대한 docs를 읽다가 첫 줄에서부터 막혀버렸다.

Component that wraps platform ScrollView while providing integration with touch locking "responder" system.

저 문장에서 responder가 뭔데?
그래서 일단 responder에 대한 지식부터 습득하고 가기로 했다.

Gesture Responder System이란?

Gesture Responder System(제스쳐 응답자 시스템)은 앱에서 일어나는 제스쳐들의 라이프싸이클을 관리해주는 시스템이다. 사용자의 터치 제스쳐는 다양한 모양으로 일어날 수 있다. 예를 들어, 앱에서 일어나는 터치가 스크롤링 동작인지, 위젯을 슬라이딩 하는건지, 탭하는건지 등의 동작을 구별해야 한다. 이것은 터치가 일어나는 동안에도 바뀔 수 있다. 혹은 동시에 여러 터치가 일어날 수도 있다.
터치 응답자 시스템은 이런 터치 상호작용에 대해 컴포넌트가 (그 컴포넌트의 부모나 자식 컴포넌트에 대한 정보 없이) 적절히 협의(?)를 볼 수 있도록 해줘야 한다.

Best Practices

앱 사용감을 좋게 하기 위해선 각 액션들은 다음 특성을 가지고 있어야 한다.

  • 피드백/하이라이팅 : 사용자에게 지금 터치가 어떤 것을 핸들링하고 있는지, 그 제스쳐 이후에 어떤 일이 발생할지를 보여주는 역할
  • 취소 가능성 : 사용자가 어떤 액션을 만들고있을 때, 그 중간쯤에 손가락을 떼버림으로써 그 액션을 취소할 수 있어야 한다.

위의 두 기능이 사용자가 앱을 사용할 때 더 편안함을 느낄 수 있게 해준다. (미리 수행 될 동작에 대한 정보를 보여주거나 취소할 수 있게 해줌으로써 실수를 방지할 수 있게 해주기 때문에)

TouchableHighlight과 Touchable

응답자 시스템은 사용하기엔 좀 복잡할 수 있다. 그래서 React Native에선 '탭 가능해야하는' 것들을 위해 추상적인 Touchable 구현체를 제공한다. 이 구현체는 응답 시스템을 사용하고, 우리가 탭 상호작용에 대해 declarative하게 구성할 수 있게 해준다. 버튼이나 링크를 사용할 부분에서 <TouchableHighlight>을 사용하면 된다.

응답자 라이프싸이클(Responder Lifecycle)

정확한 협상 메소드들을 구현해줌으로써 뷰는 '터치 응답자'가 될 수 있다. 뷰가 응답자가 되길 원하는지 물어보는 메소드 두 가지가 있다.

  • View.props.onStartShouldSetResponder: (evt) => true : 이 뷰가 터치가 시작될 때 응답자가 되길 원하는가?
  • View.props.onMoveShouldSetResponder: (evt) => true : 뷰가 응답자가 아닐 경우에 터치 움직임이 일어나는 모든 순간마다 불리는 메소드이다. 이 뷰가 터치에 대한 책임을 '주장'하길 워하는가? (번역 참..)

만약 뷰가 true를 리턴하면서 응답자가 되길 원한다면, 다음 중 하나가 일어나게 된다.

  • View.props.onResponderGrant: (evt) => {} : 뷰는 이제 터치 이벤트에 대해 반응하고 있다. 하이라이팅을 통해 유저에게 어떤 일이 일어나고있는지 보여줄 때이다.
  • View.props.onResponderReject: (evt) => {} : 다른 무언가가 응답자이고 계속 그것이 응답자일 상태이다.

만약 뷰가 응답중이라면, 다음 handler들이 불리게 된다.

  • View.props.onResponderMove: (evt) => {} : 유저가 손가락을 움직이고 있다.
  • View.props.onResponderRelease: (evt) => {} : 'touchUp'이라고도 하는 터치가 끝나는 시점에 불린다.
  • View.props.onResponderTerminationRequest: (evt) => true : 다른 무언가가 응답자가 되길 원한다. 이 뷰는 응답자를 놓아줄 것인가? 만약 true를 리턴하면 놓아주게 된다.
  • View.props.onResponderTerminate: (evt) => {} : 뷰에 있던 응답자가 다른곳으로 가져가졌다. onResponderTerminationRequest 콜 이후에 다른 뷰로부터 가져가졌을 수도 있고, OS에 의해 강제로 가져가졌을 수 있다(iOS의 컨트롤센터나 알림센터에 의해 발생한다).

evt는 다음과 같은 형식의 종합적인 터치 이벤트이다.

  • nativeEvent
    • changedTouches - Array of all touch events that have changed since the last event
    • identifier - The ID of the touch
    • locationX - The X position of the touch, relative to the element
    • locationY - The Y position of the touch, relative to the element
    • pageX - The X position of the touch, relative to the root element
    • pageY - The Y position of the touch, relative to the root element
    • target - The node id of the element receiving the touch event
    • timestamp - A time identifier for the touch, useful for velocity calculation
    • touches - Array of all current touches on the screen

ShouldSet 핸들러들

onStartShouldSetResponder와 onMoveShouldSetResponder는 가장 깊은 노드가 불린 곳 부터 버블링패턴으로 불린다. 이것은 여러 개의 뷰들이 *ShouldSetResponder 핸들러에 대해 true를 리턴했을 땐 가장 깊은 레벨의 컴포넌트가 응답자가 된다는 것을 의미한다. 이것이 대부분의 경우에 바람직한 상태이다. 모든 control들과 button들을 사용가능하게 만들어주기 때문이다.

그러나, 가끔 부모 뷰가 응답자가 되길 바랄 때도 있다. 이것은 capture phase를 사용해서 컨트롤할 수 있다. 응답자 시스템이 가장 깊은 컴포넌트로부터 버블링 되기 전에, 단계를 캡쳐해서 on*ShouldSetResponderCapture이벤트를 발생시킨다. 그러니까 만약 부모 뷰가 자식이 터치의 시작시점에 응답자가 되는 것을 막고 싶을 때, true를 반환해주는 onStartShouldSetResponderCapture 핸들러를 갖고 있으면 된다.

  • View.props.onStartShouldSetResponderCapture: (evt) => true
  • View.props.onMoveShouldSetResponderCapture: (evt) => true

PanResponder

더 높은 레벨의 제스쳐 상호작용을 원한다면 PanResponder를 보기를..(이건 나중에 봐야지)

Ref: https://reactnative.dev/docs/gesture-responder-system#panresponder

profile
4년차 프론트엔드 개발자입니다.

0개의 댓글