티스토리 뷰
안녕하세요 :) 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에 갖다대야하는 손가락 수입니다.
panGestureRecognizer.minimumNumberOfTouches = 2
(panGestureRecognizer는 저의 Pan Gesture Recognizer의 IBOutlet입니다. )
그러니까 이렇게 지정이 되면!!!!!!
야 Pan Gesture를 인식하긴 하는데, 최소!! 최소 손가락 2개는 View에 갖다대야해. ㅇㅋ??/?/
=====> 이 말은 무슨뜻??? ===> 2개이상이기만 하면!!!!!!!!!!!!!!!!!! Pan Gesture로 인식한다는 소리죠.
2개든 3개든 4개든요.
이쯤에서 자연스럽게 한가지 사실을 알 수 있습니다. minimumNumberOfTouches의 default값은 1입니다 ㅎㅎ
panGestureRecognizer.maximumNumberOfTouches = 2
그럼 이건? maximumNumberOfTouches는?
뭔가 감이 오시나요?
야 Pan Gesture를 인식하긴 하는데, 나는 최대!!!!!!최대!!! 2개까지만 허용할게.
=====> 이 말은 무슨뜻??? ===> 딱 손가락 2개까지만 Pan Gesture로 인식하겠다는 소리입니다. 즉, 손가락 1개, 손가락 2개까지만 Pan Gesture로 허용한다는 소리죠.
조금 아시겠나요!?!?!?
그리고 위에서 "자신의 Action메소드에서 UIPanGestureRecognizer객체에 현재 Gesture의 변환 (translation(in:)) 및 변환속도 (velocity(in:))를 쿼리할 수 있습니다."
라고 했는데 이것도 뭔소린지 1도 모르겠는 부분이죠?
저 메소드들이 무슨 일을 하는지 알아봅시다.
먼저, translation.
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에 대한 설명입니다.
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을 적용해줘야합니다.
그럼 해볼까요?
@IBAction func panAction(_ sender: Any) {
}
늘 그렇듯이 Pan Gesture Recognizer의 IBAction을 만들어주었어요.
자 이제 1번을 해봅시다. 자 천천히 따라오세요.
func translation(in view: UIView?) -> CGPoint
translation에서 파라미터로 받는 view는 우리가 변환을 계산해야하는 view였어요. 그렇다면!!! 저 view에 우리의 이미지view를 넣어주면 되겠네요.
그러면 저 Pan Gesture를 인식하면, 우리의 이미지가 이미지view의 슈퍼view에서의 새로운 좌표가 리턴됩니다.
@IBAction func panAction(_ sender: Any) {
let transition = panGestureRecognizer.translation(in: imageView)
}
translation에서 리턴값이 뭐였죠? 네. CGPoint값입니다. 그럼 저 translation이란 상수는 x와 y프로퍼티를 가지고 있겠네요.
그렇다면!!!!!이제 뭘해줘야할까요.
@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을 해주지 않아서 그렇습니다.
@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}이지만, 현재 손가락으로 드래그 하여 끌어온 위치의 벡터를 나타냅니다.
따라서, 드래그 한 거리를 결정하는 데 필요한 것은 이 벡터의 또 다른 점 뿐입니다.
let transition = panGestureRecognizer.translation(in: imageView)
(또다르 점은 translation메소드로 얻어온 값을 의미)
translation메소드로 받은 리턴값으로 부터 새로운 view의 위치를 설정할 수 있었습니다.
그러나, UIPanGestureRecognizer는 실제로 "지속적인 리포터(continuous reporter)"(계속 보고한다는 뜻) 이며 마지막 보고 이후의 상태를 잊지않습니다.
UIPanGestureRecognizer는 우리가 translation의 부분을 다 써버린 것을 모릅니다.
그래서 다음번에 UIPanGestureRecognizer의 Action메소드가 호출되면, translation은 마지막으로 손가락을 이동한 위치에서부터 계산되지 않고, 손가락을 끌기 시작한 원래 장소!! (즉, 처음 Pan Gesture를 시작한 위치)
에서 계산됩니다.
==> 그래서 조금 움직였음에도 불구하고 이미지가 확 내려갔던것이죠.
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.)
이게 이제 무슨소리인지 알겠죠????진심 오늘의 핵심중의 핵심이라고 할 수 있는 부분입니ㅏ다.
그리고 하나 안본게 있는데, 위에서 언급한 속도입니다.
func velocity(in view: UIView?) -> CGPoint
속도도 어떤 메소드인지 확인해보겠습니다.
그냥 간단하게 말하면!!!! Pan Gesture의 속도에요.
리턴값은 Pan Gesture의 속도로, 초당 포인트(점)로 표시됩니다. 속도는 수평 및 수직 구성요소로 나뉩니다.
그래서 저는 velocity메소드를 보고..아..; 그러니까 내가 드래그를 빨리하는지 느리게 하는지 나타내주는건가..?근데 콘솔에 로그를 찍어봐도 너무 많이 나와서 (Pan Gesture Action메소드가 진짜 많이 불려요 조금 움직여도..) 뭐가 뭔지 모르겠는겁니다...그리고 속도인데 왜 (x,y)로 나와...
CGFloat이면 모를까.....이게 머얌..ㅎ
했습니다.
그런데 찾아보니까!!!! 이 velocity으로 내가 움직이는 "방향"을 알 수 있습니다.
자...잘 이해가 안가시죠? velocity라는 뜻이 뭔가요. 속력이 아니라!!!! 속도입니다.
속도의 개념을 다들 아시나요?
출처 : 네이버 지식백과
자..속력과 속도의 차이점은..바로 속도는 방향을 가지고 있다는 점입니다.
그래서 이 velocity로 내가 지금 어느방향으로 움직이고 있는지 알 수 있어요!!
8방을 확인할 수 있어요. 근데 저는 일단 상하좌우만 판단해볼게요 :)
let velocity = panGestureRecognizer.velocity(in: imageView)
먼저 우리의 Pan Gesture Recognizer객체에서 velocity를 얻어옵시다. 얻어오긴 얻어올건데!!우리의 imageView에서 얻어올거라고 파라미터에 넣어줬죠?
그럼 velocity메소드는 CGPoint를 리턴하기때문에 x와 y프로퍼티를 가지고 있을거에요.
이제 방향을 알아봅시다.
if abs(velocity.x) > abs(velocity.y) {
//좌우 판단
}
자...생각을 해보세요 천천히
x좌표는 우리가 생각하듯이 가로?라고 생각하시고 y는 세로라고 생각합니다.
근데 그 절댓값, 즉 움직인 절대적인 값이 x가 더 크다? 그럼 Horizontal. 즉 수평적인 Panning이라고 판단할 수 있겠네요.
그럼 이 if문 안에서 왼쪽인지 오른쪽인지 구분하면 됩니다.
if abs(velocity.x) > abs(velocity.y) {
velocity.x < 0 ? print("left") : print("right")
}
자....수평선을 생각해보세요. 거기에 우리가 손가락을 대고 있는겁니다. 내가 손가락을 왼쪽으로 옮긴다? ===> x좌표가 줄어든다. 왼쪽으로 갈 수록 -값이니까.
즉 왼쪽. 현재 손가락의 위치에서 조금이라도 왼쪽으로 가면 -값이 나오게 됩니다. 즉 이 velocity의 x프로퍼티가 -임은 왼쪽으로 pan한다고 판단할 수 있죠. 조금이라도 오른쪽으로 움직이게 되면 x프로퍼티가 +값을 띄게됩니다. 그러면 오른쪽이라고 판단할 수 있죠.
그렇다면 이제 상하 판별만 남았네요. 그러면 좌우판별과는 다르게...절대적인 y값이 절대적인 x값보다 더 크다면 상하로 움직이고 있다고 판단할 수 있게 됩니다.
else if abs(velocity.y) > abs(velocity.x) {
// 상하 판단
}
그렇죠? 그럼 이제 또 생각해봅시다. 어떨때 "위"라고 할 수 있고, 어떨 때 "아래"라고 할 수 있죠?
수직좌표계를 그냥 생각하세요. 내가 손가락을 대고 있습니다. y값이 조금이라도 위로 움직이면 0보다 작아지게 되고, 조금이라도 아래로 움직이면 0보다 커집니다. (위로 갈수록 값이 커진다고 생각하면 안됩니다.)
else if abs(velocity.y) > abs(velocity.x) {
velocity.y < 0 ? print("up") : print("down")
}
이렇게요.
XD
와 글이 이렇게 길어질줄이야 zzzz
Pan Gesture로 정말 많은 것을 할 수 있다는 것을 알았네요 :)
저에겐 엄청 많이 도움이 됐는데..이 글이 Pan Gesture를 이해하는데 도움이 되었으면 좋겠네요XD
'iOS' 카테고리의 다른 글
iOS ) View/레이아웃 업데이트 관련 메소드 (12) | 2017.12.28 |
---|---|
iOS ) ScrollView에서 위아래 Gesture를 감지하고싶다면? / UIGestureRecognizerDelegate (1) | 2017.12.26 |
iOS ) Swift에서의 namespace (0) | 2017.12.20 |
iOS ) Archive시 No such module에러가 난다면? (0) | 2017.12.20 |
iOS ) Today Extension에서 설정한 이미지가 안보인다면? (0) | 2017.12.19 |
- Combine
- FLUTTER
- np-hard
- swift3
- iOS delegate
- 회고
- WKWebView
- actor
- WWDC
- fastlane
- IOS
- WidgetKit
- Xcode
- swift sort
- np-complete
- Git
- 피아노
- swift array
- swift 공부
- swift delegate
- 스위프트
- Swift
- 스위프트 문법
- SwiftUI
- Accessibility
- UIBezierPath
- github
- ios 13
- 제이슨 파싱
- swift tutorial
- Total
- Today
- Yesterday