티스토리 뷰

iOS

iOS ) Gesture Recognizer - Pan

Zedd0202 2017. 12. 22. 16:34
반응형

안녕하세요 :) Zedd입니다.

Pan Gesture Recognizer를 해보겠습니댜 키키


Pan Gesture Recognizer



자!!!! 역시나 먼저 Pan Gesture Recognizer에 대한 애플문서를 보는게 좋겠죠? 

솔직히 Pan Gesture가 뭔지 모르시는 분들도 계실거에요 :) 

저도임 ㅎ

하나하나 알아가봅시다. 이 Pan Gesture가 뭔지요!!

Pan Gesture Recognizer에 대한 정의는 

"A concrete subclass of UIGestureRecognizer that looks for panning (dragging) gestures."

"패닝(드래그)하는 Gesture를 찾으며, UIgestureRecognizer의 구체적 하위클래스입니다."


아하 우리한테는 Pan(panning)이란 단어보다는 드래그(dragging)가 더 익숙하네요.

아직도 이 Pan Gesture가 뭔지 잘 모르시는 분들도 계실 것 같아서 움짤로 보여드리겠습니다.





짠! 이것이 바로 Pan Gesture에요. 



Pan은 이런뜻을 가지고 있답니다. 그러니까 간단하게 말해서 Pan Gesture Recognizer는 제 손가락이 상하좌우로 움직이는 것을 인식하는것이죠.


Pan Gesture에 대해서 더 자세히 알아봅시다. 

사용자가 화면을 상하좌우로 이동하는 동안, View에 하나 이상의 손가락을 눌러야합니다.

이 Gesture Recognizer에 대한 동작 방법을 구현하는 클라이언트는, 제스쳐의 현재 변환 및 속도를 요청할 수 있습니다.

패닝 동작은 게속됩니다. 허용된 최소 손가락 수(minimumNumberOfTouches)가 Pan Gesture로 간주 될 만큼 충분히 움직였을 때 시작됩니다.(began)

최소 손가락 수를 누르면서, 손가락을 움직이면, 변경(changed)됩니다. 모든 손가락을 들러올리면 끝납니다. (ended)

이 클래스의 클라이언트는 자신의 Action메소드에서 UIPanGestureRecognizer객체에 현재 Gesture의 변환 (translation(in:)) 및 변환속도 (velocity(in:))를 쿼리할 수 있습니다.

변환 및 속도값에 좌표 시스템을 사용해야하는 View를 지정할 수 있습니다. 클라이언트는 변환을 원하는 값으로 재설정할 수도 있습니다. 


흐음...다른 제스쳐와 다르게 무슨소리를 하는지 잘 모르겠네요. 

오늘은 Pan Gesture Recognizer에서 부를 수 있는 프로퍼티들과 메소드들을 더 살펴볼게요.

먼저, 이건 아시겠죠

Pan Gesture Recognizer는 일단 동작하려면 View에 하나이상의 손가락을 눌러야한다는 것. 

하나이상.

두개가 될 수 있다는 말이네요? 네 그렇습니다!!!!!!!!!!!!!!!!

그래서 위에서 언급했죠. 최소 손가락 수(minimumNumberOfTouches).

이 minimumNumberOfTouches를 지정할 수 있습니다. 또한 maximumNumberOfTouches도 지정할 수 있죠. 

엥;;; 둘이 뭔차이냐;;;

일단 이름에서 뭔가 느낌적인 느낌이 옵니다. 미니멈과 맥시멈.

바로 최소와 최대죠. Apple이 정의하는 이 둘의 정의를 보겠습니다. 

minimumNumberOfTouches : 이 Gesture를 인식하기 위해 View를 터치할 수 있는 최소 손가락 수

maximumNumberOfTouches : 이 Gesture를 인식하기 위해 View를 터치할 수 있는 최대 손가락 수

ㅎ뭔솔


간단하게 말씀드리겠습니다. minimumNumberOfTouches는 이 Pan Gesture를 인식하기 위해서 "최소한으로" View에 갖다대야하는 손가락 수입니다. 


  1. panGestureRecognizer.minimumNumberOfTouches = 2


(panGestureRecognizer는 저의 Pan Gesture Recognizer의 IBOutlet입니다. )


그러니까 이렇게 지정이 되면!!!!!!


야 Pan Gesture를 인식하긴 하는데, 최소!! 최소 손가락 2개는 View에 갖다대야해. ㅇㅋ??/?/

=====> 이 말은 무슨뜻??? ===> 2개이상이기만 하면!!!!!!!!!!!!!!!!!! Pan Gesture로 인식한다는 소리죠.

2개든 3개든 4개든요. 

이쯤에서 자연스럽게 한가지 사실을 알 수 있습니다. minimumNumberOfTouches의 default값은 1입니다 ㅎㅎ


  1. panGestureRecognizer.maximumNumberOfTouches = 2


그럼 이건? maximumNumberOfTouches는? 

뭔가 감이 오시나요?


야 Pan Gesture를 인식하긴 하는데, 나는 최대!!!!!!최대!!! 2개까지만 허용할게. 

=====> 이 말은 무슨뜻??? ===> 딱 손가락 2개까지만 Pan Gesture로 인식하겠다는 소리입니다. 즉, 손가락 1개, 손가락 2개까지만  Pan Gesture로 허용한다는 소리죠. 

조금 아시겠나요!?!?!?

그리고 위에서 "자신의 Action메소드에서 UIPanGestureRecognizer객체에 현재 Gesture의 변환 (translation(in:)) 및 변환속도 (velocity(in:))를 쿼리할 수 있습니다."

라고 했는데 이것도 뭔소린지 1도 모르겠는 부분이죠?

저 메소드들이 무슨 일을 하는지 알아봅시다.

먼저, translation.


  1. func translation(in view: UIView?) -> CGPoint


이게 translation메소드의 원형입니다. 

UIView타입을 파라미터로 받고, CGPoint타입을 리턴하네요.

translation의 설명을 하자면, 명시된 View(즉, 파라미터로 받은 view)의 좌표 시스템에서의 pan gesture의 변환(translation)입니다. 

그래서 도대체 뭘 변환하는데;;??


일단 translation을 더 살펴볼까요.

파라미터로 받는 view는 좌표 시스템에서 pan gesture의 변환을 계산해야하는 view입니다. view의 위치를 조정하여, 사용자의 손가락 아래에 유지하려면(사용자의 손가락을 따라 해당 view가 따라오게 할려면) 해당 view의 슈퍼View(부모view) 좌표계에서 translation을 요청하십시오.


반환값 : 지정된 슈퍼View(부모view)의 좌표계에서, view의 새위치를 식별하는 점(point, 즉 x,y좌표.)


x및 y값, 즉 반환된 CGPoint는 시간에 따른 전체 translation을 보고(report)합니다. 

이 값은 변환이 마지막으로 보고된 시점의 델타값이 아닙니다. 

Pan Gesture를 처음 인식할 때 변환 값을 view상태에 적용합니다.  즉 핸들러가 호출될 때마다 값을 연결하지 마십시오.(do not concatenate the value each time the handler is called.)


다음은 setTranslation에 대한 설명입니다. 


  1. func setTranslation(_ translation: CGPoint, in view: UIView?)


뭔가 이름만 봐도 translation을 set하는 그런 메소드같네요. 

이 setTranslation은 지정된 view의 좌표계에서 translation value(변환값)을 설정합니다.

일단 파라미터를 2개를 받네요!!

translation이랑, view.

뭔가.. translation은 우리 위에서 봤는데? translation메소드의 리턴값이 CGPoint였는데?

그렇습니다. 파라미터로 받는 translation은 새 변환 값을 식별하는 x, y좌표이며

view는 좌표계에서 변환이 일어날 view입니다. 


그렇다면 우리가 해야할일이 뭔지 조금 알 수 있을 것 같습니다.


1. 먼저 translation메소드를 통해 이 Pan Gesture로 인해 변환되는 좌표 값을 얻어와야합니다. 

2. 그리고 우리의 이미지View에 이 translation을 적용해줘야합니다.


그럼 해볼까요? 


  1.  @IBAction func panAction(_ sender: Any) {

    }



늘 그렇듯이 Pan Gesture Recognizer의 IBAction을 만들어주었어요. 


자 이제 1번을 해봅시다. 자 천천히 따라오세요. 


  1. func translation(in view: UIView?) -> CGPoint


translation에서 파라미터로 받는 view는 우리가 변환을 계산해야하는 view였어요. 그렇다면!!! 저 view에 우리의 이미지view를 넣어주면 되겠네요.

그러면 저 Pan Gesture를 인식하면, 우리의 이미지가 이미지view의 슈퍼view에서의 새로운 좌표가 리턴됩니다. 


  1.  @IBAction func panAction(_ sender: Any) {


           let transition = panGestureRecognizer.translation(in: imageView)

    }




translation에서 리턴값이 뭐였죠? 네. CGPoint값입니다. 그럼 저 translation이란 상수는 x와 y프로퍼티를 가지고 있겠네요.

그렇다면!!!!!이제 뭘해줘야할까요. 



  1.  @IBAction func panAction(_ sender: Any) {


           let transition = panGestureRecognizer.translation(in: imageView)

           let changedX = imageView.center.x + transition.x

           let changedY = imageView.center.y + transition.y

           imageView.center = CGPoint(x: changedX, y: changedY)

    }



네. 현재 이미지view가 있는 위치가!!!!!!!있겠죠?!!! 그 위치도 역시 CGPoint일 것입니다. 그 이미지View가 있는 중앙값이 center라는 프로퍼티로 불러올 수 있는데요. 이 center역시 CGPoint타입입니다. 그러므로 x와 y프로퍼티를 가지고 있죠.

그리고!!!!!!!!!!!!!각각에다 우리가 Pan Gesture인식하여 새롭게 가야할 좌표를 더해주는 거죠.

그리고 그 이미지View의 center를 새로운 x, y값으로 넣어줘야합니다. 

아하!!!!!!이렇게 하면 다 끝난 것 같지만...이렇게 하고 실행을 하면..




;;; 굉장히 조금 움직였음에도 불구하고 이미지가 확 내려가게 되죠. 

엥;;; 모지

네 그이유는 우리가 위에서 배운 setTranslation을 해주지 않아서 그렇습니다. 



  1.  @IBAction func panAction(_ sender: Any) {


           let transition = panGestureRecognizer.translation(in: imageView)

           let changedX = imageView.center.x + transition.x

           let changedY = imageView.center.y + transition.y

           imageView.center = CGPoint(x: changedX, y: changedY)


           panGestureRecognizer.setTranslation(CGPoint.zero, in: imageView)

    }



이렇게 Pan Gesture Recognizer객체에 setTranslation을 적용해야줘야합니다.

setTranslation메소드가 뭘 한다고 그랬죠? 

지정된 view의 좌표계에서 translation 값을 설정합니다.

이걸 매번 꼭 해줘야 한다고 그러더라구요. 


자..일단 궁금한점이 많으실거에요. 

저도 궁금했습니다.

일단


1. 이 setTranslation을 왜 매번 해줘야하는지

2. 왜 translation파라미터에 CGPoint.zero 즉, (0,0)이 들어가는지


가 궁금했습니다. 


일단 2번을 아시면 1번이 자연스럽게 이해가 가실겁니다.

UIPanGestureRecognizer의 translation은, 이 벡터의 원래 위치가 {0,0}이지만, 현재 손가락으로 드래그 하여 끌어온 위치의 벡터를 나타냅니다. 

따라서, 드래그 한 거리를 결정하는 데 필요한 것은 이 벡터의 또 다른 점 뿐입니다. 


  1. let transition = panGestureRecognizer.translation(in: imageView)


(또다르 점은 translation메소드로 얻어온 값을 의미)

translation메소드로 받은 리턴값으로 부터 새로운 view의 위치를 설정할 수 있었습니다.

그러나, UIPanGestureRecognizer는 실제로 "지속적인 리포터(continuous reporter)"(계속 보고한다는 뜻) 이며 마지막 보고 이후의 상태를 잊지않습니다.

UIPanGestureRecognizer는 우리가 translation의 부분을 다 써버린 것을 모릅니다. 

그래서 다음번에 UIPanGestureRecognizer의 Action메소드가 호출되면, translation은 마지막으로 손가락을 이동한 위치에서부터 계산되지 않고, 손가락을 끌기 시작한 원래 장소!! (즉, 처음 Pan Gesture를 시작한 위치)

에서 계산됩니다.

==> 그래서 조금 움직였음에도 불구하고 이미지가 확 내려갔던것이죠.


  1. panGestureRecognizer.setTranslation(CGPoint.zero, in: imageView)


그래서 우리는 위 메소드를 0.0으로 호출하여 UIPanGestureRecognizer에게 새로운 드래그 동작을 시작할 것임을 알리는 것이죠.

우리가 드래그를 이어서 한다고 해서 드래그가 한~~~번~~ 이런게 아닙니다.

UIPanGestureRecognizer의 Action메소드는 지속적이라고 그랬죠? 계속 Action메소드를 호출하게 됩니다. 한번 Action메소드가 호출될때마다 파라미터로 보내는 view의 translation을 0,0으로 set해줘야만하는것이죠. 안그러면 우리가 처음 드래그를 시작한 그 위치!!!에서 부터 계속 거리를 계산할것이니까요. 

이제 

1. 이 setTranslation을 왜 매번 해줘야하는지

2. 왜 translation파라미터에 CGPoint.zero 즉, (0,0)이 들어가는지

가 이해가 조금 가시죠?


그래서 왜!!! 위에서 


x및 y값, 즉 반환된 CGPoint는 시간에 따른 전체 translation을 보고(report)합니다

(시간에 따른 전체 translation이 핵심)


이 값은 변환이 마지막으로 보고된 시점의 델타값이 아닙니다. 

Pan Gesture를 처음 인식할 때 변환 값을 view상태에 적용합니다.  즉 핸들러가 호출될 때마다 값을 연결하지 마십시오.(do not concatenate the value each time the handler is called.)


이게 이제 무슨소리인지 알겠죠????진심 오늘의 핵심중의 핵심이라고 할 수 있는 부분입니ㅏ다.


그리고 하나 안본게 있는데, 위에서 언급한 속도입니다.


  1. func velocity(in view: UIView?) -> CGPoint


속도도 어떤 메소드인지 확인해보겠습니다. 

그냥 간단하게 말하면!!!! Pan Gesture의 속도에요.

리턴값은 Pan Gesture의 속도로, 초당 포인트(점)로 표시됩니다. 속도는 수평 및 수직 구성요소로 나뉩니다. 


그래서 저는 velocity메소드를 보고..아..; 그러니까 내가 드래그를 빨리하는지 느리게 하는지 나타내주는건가..?근데 콘솔에 로그를 찍어봐도 너무 많이 나와서 (Pan Gesture Action메소드가 진짜 많이 불려요 조금 움직여도..) 뭐가 뭔지 모르겠는겁니다...그리고 속도인데 왜 (x,y)로 나와...

CGFloat이면 모를까.....이게 머얌..ㅎ

했습니다.


그런데 찾아보니까!!!! 이 velocity으로 내가 움직이는 "방향"을 알 수 있습니다. 

자...잘 이해가 안가시죠? velocity라는 뜻이 뭔가요. 속력이 아니라!!!! 속도입니다.

속도의 개념을 다들 아시나요?


출처 : 네이버 지식백과


자..속력과 속도의 차이점은..바로 속도는 방향을 가지고 있다는 점입니다. 

그래서 이 velocity로 내가 지금 어느방향으로 움직이고 있는지 알 수 있어요!!


8방을 확인할 수 있어요. 근데 저는 일단 상하좌우만 판단해볼게요 :)


  1.  let velocity = panGestureRecognizer.velocity(in: imageView)


먼저  우리의 Pan Gesture Recognizer객체에서 velocity를 얻어옵시다. 얻어오긴 얻어올건데!!우리의 imageView에서 얻어올거라고 파라미터에 넣어줬죠?

그럼 velocity메소드는 CGPoint를 리턴하기때문에 x와 y프로퍼티를 가지고 있을거에요.

이제 방향을 알아봅시다.


  1.  if abs(velocity.x) > abs(velocity.y) { 

        //좌우 판단

    }


자...생각을 해보세요 천천히

x좌표는 우리가 생각하듯이 가로?라고 생각하시고 y는 세로라고 생각합니다. 

근데 그 절댓값, 즉 움직인 절대적인 값이 x가 더 크다? 그럼 Horizontal. 즉 수평적인 Panning이라고 판단할 수 있겠네요.

그럼 이 if문 안에서 왼쪽인지 오른쪽인지 구분하면 됩니다.

 

  1.  if abs(velocity.x) > abs(velocity.y) { 

        velocity.x < 0 ? print("left") :  print("right")

    }


자....수평선을 생각해보세요. 거기에 우리가 손가락을 대고 있는겁니다. 내가 손가락을 왼쪽으로 옮긴다? ===> x좌표가 줄어든다. 왼쪽으로 갈 수록 -값이니까.

즉 왼쪽. 현재 손가락의 위치에서 조금이라도 왼쪽으로 가면 -값이 나오게 됩니다. 즉 이 velocity의 x프로퍼티가 -임은 왼쪽으로 pan한다고 판단할 수 있죠. 조금이라도 오른쪽으로 움직이게 되면 x프로퍼티가 +값을 띄게됩니다. 그러면 오른쪽이라고 판단할 수 있죠. 

그렇다면 이제 상하 판별만 남았네요. 그러면 좌우판별과는 다르게...절대적인 y값이 절대적인 x값보다 더 크다면 상하로 움직이고 있다고 판단할 수 있게 됩니다. 


  1. else if abs(velocity.y) > abs(velocity.x) { 

        // 상하 판단

    }


그렇죠? 그럼 이제 또 생각해봅시다. 어떨때 "위"라고 할 수 있고, 어떨 때 "아래"라고 할 수 있죠?

수직좌표계를 그냥 생각하세요. 내가 손가락을 대고 있습니다. y값이 조금이라도 위로 움직이면 0보다 작아지게 되고, 조금이라도 아래로 움직이면 0보다 커집니다. (위로 갈수록 값이 커진다고 생각하면 안됩니다.)


  1. else if abs(velocity.y) > abs(velocity.x) { 

        velocity.y < 0 ? print("up") :  print("down")

    }


이렇게요.



XD


와 글이 이렇게 길어질줄이야 zzzz

Pan Gesture로 정말 많은 것을 할 수 있다는 것을 알았네요 :)

저에겐 엄청 많이 도움이 됐는데..이 글이 Pan Gesture를 이해하는데 도움이 되었으면 좋겠네요XD


반응형