[react-native-svg] Android onPress Bug (feat. svg-path-properties)

하태현·2021년 8월 23일
1

react-native-svg로 작업을 하는데 <g>태그에 onPress를 사용하고 싶었다.

아래의 그림의 동그란 부분을 버튼화 시키고 싶었던 것이다. 이렇게 svg를 다루는 일이 많을 줄 몰랐는데 아이들을 대상으로 하는 앱을 만들고 있어서 하루종일 SVG와 사투를 벌이고 있다.

코드의 시나리오를 간단하게 설명 하자면 10개의 지점이 있고 클리어된 지점은 파란색으로 표시 할 것이다.(세이브 포인트? 같은 느낌)
궁금해 하실지 모르겠지만 원들이 이어지는 노란색 길은 배경에 속하도록 만들었다.
로직에 따라서 변동이 있는 부분이 아니라 따로 svg파일로 빼서 관리를 했다. (이유는 스크린 코드에 들어가기엔 너무 코드가 길다.... 2800줄이 넘는다 ㅋㅋㅋㅋ)
어쨌든 본론은 동그란 부분을 터치 하면 해당 버튼의 index를 콘솔에 출력하고 싶었다.

Screen : 10개의 원들을 그릴 스크린
ActivityButton : 노란 동그라미 부분이다. 미완료 시(진한 노란 원), 완료 시(파란색점+파란 원)
BackgroundSVG : 로직이 필요한 부분을 제외한 나머지 이미지 영역을 따로 분리

제일 어려웠던 부분은 "ActivityButton을 어떻게 해당 위치에 찍어줄 수 있을까?" 였다.

const Screen = () => {
  const [today] = useState(3);
  const [todayClear] = useState(false);

  return (
    <View style={styles.frame}>
      ...
      <Svg width={height} height={width} viewBox="0 0 812 375" fill="none">
        <BackgroundSVG />
        {points.map((p, i) => (
          <ActivityButton key={i} x={p.x} y={p.y} today={today} index={i} todayClear={todayClear} />
        ))}
        ...
      </Svg>
    </View>
  );
}

const ActivityButton = ({x, y, today, index, todayClear}: ActivityButtonProps) => {
  const clear = todayClear ? today : today - 1;

  return (
    <G x={x - 21} y={y - 42} onPress={() => console.log(index)}> // bug point!!
      <Circle cx={21} cy={21} r="30" />  // 터치 영역을 넓히기 위한 부분
      <Path
        d="M21.1979 41.4027C32.6118 41.4027 41.8646 32.1499 41.8646 20.736C41.8646 9.32212 32.6118 0.0693359 21.1979 0.0693359C9.78403 0.0693359 0.53125 9.32212 0.53125 20.736C0.53125 32.1499 9.78403 41.4027 21.1979 41.4027Z"
        fill="#FFF4B3"
      />
      <Path
        d="M21.201 30.7363C26.7238 30.7363 31.201 26.2592 31.201 20.7363C31.201 15.2135 26.7238 10.7363 21.201 10.7363C15.6781 10.7363 11.201 15.2135 11.201 20.7363C11.201 26.2592 15.6781 30.7363 21.201 30.7363Z"
        fill={clear >= index ? '#00CEFF' : '#F9DF3F'}
      />
      {clear >= index && (
        <Path
          d="M21.2015 24.07C23.0424 24.07 24.5348 22.5776 24.5348 20.7367C24.5348 18.8957 23.0424 17.4033 21.2015 17.4033C19.3605 17.4033 17.8681 18.8957 17.8681 20.7367C17.8681 22.5776 19.3605 24.07 21.2015 24.07Z"
          fill="#0084D0"
        />
      )}
    </G>
  );
};

제가 설명하고 싶었던 부분은 이 부분인데 서론이 너무 길었네요.
<G x={x - 21} y={y - 42} onPress={() => console.log(index)}>
ios 시뮬레이터와, android 에뮬레이터에서 모두 정상동작 하는데, 실제 안드로이드 디바이스에서
onPress가 동작하지 않는 것이다. 그래서 찾아보니 같은 문제를 react-native-svg/issues에서 찾을 수 있었다.

해결방법

PanResponder

react-native에서 제공하는 API이고,
특정 제스쳐에 관한 핸들러를 지정할 수 있다.
Draggable Component 를 만들때 유용하다. (RN 공식 홈페이지에 예시로 나와있다.)
다양한 용법이 있지만 여기선 필요한 부분만 쓰도록 하겠다.
아래의 방법으로 간단하게 해결할 수 있다. 물론 onPress가 훨씬 간단하지만 이 글을 읽으시는 분들은 나와 같이 이런 상황을 마주하여 당황하지 않았으면 한다.

onStartShouldSetPanResponder: onStart(즉, 터치)에 반응 할것인가 (true/false)
onPanResponderGrant : 터치가 시작되면 콜백을 실행한다.

const ActivityButton = ({x, y, today, index, todayClear}: ActivityButtonProps) => {
  const clear = todayClear ? today : today - 1;

  const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: () => true,
    onPanResponderGrant: () => console.log(index);
  });
  
  return (
    <G x={x - 21} y={y - 42} {...panResponder.panHandlers}> // bug point!!
      ...
    </G>
  );
};

틀린 점이나 궁금한 점 있으시면 언제든지 댓글 달아주세요

profile
왜?를 생각하며 개발하기, 다양한 프로젝트를 경험하는 것 또한 중요하지만 내가 사용하는 기술이 어떤 배경과 이유에서 만들어진 건지, 코드를 작성할 때에도 이게 최선의 방법인지를 끊임없이 질문하고 고민하자. 이 과정은 앞으로 개발자로 커리어를 쌓아 나갈 때 중요한 발판이 될 것이다.

0개의 댓글