Safari iPhone에서 Touch Gesture 구현하기

황제낙엽 2012.04.13 11:08 조회 수 : 228

sitelink1 http://blog.naver.com/naverdev?Redirect=...0114853966 
sitelink2  
extra_vars3
extra_vars4 ko 
extra_vars5  
extra_vars6 sitelink1 

Touch Gesture
요 즘 대부분의 스마트폰에는 터치 입력장치를 지원합니다.  간단히 손가락 터치만으로 특정 기능을 수행할 수 있게 해 줍니다. 하지만 스마트폰의 스크린 사이즈의 한계상 단순히 누르는 방식만으로는 원하는 기능을 모두 처리할 수 없습니다. 또한 단순한 터치만으로는 직관적으로 기능을 수행할 수 없습니다.  한정된 터치 영역 내에서 직관적으로 기능을 수행하기 위해 대부분의 스마트폰 OS들이 Touch Gesture기능을 제공하고 있습니다.
Touch Gesture기능을 이용하여 확대 축소 버튼으로 화면을 가리기 보다는 두 개의 손가락을 오므렸다 펼치면서 확대 축소하거나, 삭제 버튼 대신에 쭉 그어서 아이템을 삭제하는 등, 한번만 익히면 금방 따라 할 수 있게 하고 한정된 공간을 최대한 활용 할 수 있도록 해 줍니다.

Touch Gesture의 종류 및 특성
요 즘은 디바이스적에서 multi-touch를 지원하는 기기들이 많습니다.  따라서 Gesture의 종류도 다양해 지고 있습니다.  간단히 한 손가락만으로 기능을 수행할 수 있는 Gesture, 두 손가락을 이용해서 기능을 수행 할 수 있는 Gesture 등 다양한 방식의 Gesture들을 지원할 수 있게 되었습니다.

하 지만 직관적이라는 측면에서 사람들이 생각하는 방식은 비슷한가 봅니다.  확대 축소를 위해서는 두 손가락을 이용하고, 선택을 위해서는 한번 터치 하는 등 OS가 다양함에도 불구하고 제공되는 Gesture들은 대동소이 합니다. 아직 Gesture에 대한 용어 표준안이 없어 OS마다 표현 방식이 다릅니다. 이를 정리한 글이 있어서 간단히 링크를 추가합니다. OS마다 다양한 용어로 사용되는 Gesture를 통합하여 Core Gesture라는 하나의 set으로 UX, UI 담당자들의 용어를 통일해 보려는 시도 같습니다.

Touch Gesture Reference Guide에 대한 자세한 정보는 http://www.lukew.com/ff/entry.asp?1071에서 볼 수 있습니다.

 

iPhone OS에서의 Gesture에 대해서 쓰려고 함으로 여기서는 iPhone OS에서 사용하는 용어로 정리하도록 하겠습니다.  iPhone OS에서 언급하고 있는 기본 Gesture들은 다음과 같습니다. (iPhone Human Interface Guidelines 참고)

 

 Gesture

 정의

 기 적용된 Action

 Tap

 손가락으로 터치 영역을 한번 눌렀다가 떼는 행위

 버튼이나 아이템 등을 선택

 Drag

 손가락으로 터치 영역을 누른 상태에서 움직이는 행위

 Panning or Scrolling

 Swipe

 짧게 좌에서 우로 또는 위에서 아래로 손가락을 휘젓는 행위

 리스트에서 특정 항목을 삭제 시

 Pinch Open

 두 개의 손가락을 터치 영역에 대고 벌리는 행위

 확대

 Pinch Close

 두 개의 손가락을 터치 영역에 대고 오그리는 행위

 축소

 Double Tap

 Tap을 빠른 시간 안에 두 번 수행하는 행위

 이미지 확대 / 축소

 Touch and Hold

 손가락으로 한번 누른 상태로 일정 시간을 보내는 행위

 에디터 박스에서 돋보기 UI 노출

 

이 밖에도 다양한 Gesture가 존재하겠지만, 위 Gesture는 많은 곳에서 사용되고 있고, 익히기도 쉽기 때문에 Gesture를 적용 전에 위의 것들을 적용 가능한지 먼저 확인을 해보는 것이 UX측면에서 좋다고 할 수 있을 것입니다.
위 Gesture들을 자세히 들여다 보면 한가지 중요한 특성이 있는데 그것은 Gesture를 인지했을 때 Action(기능)에 대한 수행 횟수입니다.
Tap, Double Tap, Touch and Hold의 경우 최종적으로 Gesture가 인식되었을 때 한 Action이 1회 수행됩니다.  하지만 Drag나 Pinch open/close의 경우 손가락을 움직일 때마다 계속적으로 Action을 수행하게 됩니다.
한번만 인지되어 Action을 수행하는 것을 Discrete Gestures, 계속적으로 인지하며 Action을 수행하는 것을 Continuous Gestures라고 합니다. (용어의 출처는 뒤에서 말씀 드리겠습니다.)
위 특성에 따라 이벤트 처리나 실제 코드로 구현 시 다르게 처리됩니다.

 


iPhone Touch Gesture 구현
Gesture 가 입력 방식에서 중요하게 사용되니, iPhone App.에서 쉽게 구현할 수 있으리라 생각할 수 있습니다. iPhone OS에서 기본으로 제공하는 UI Control(View)들은 자체 Gesture기능을 기본적으로 포함하고 있습니다.  리스트를 표현하는 UITableView에는 삭제를 위한 Swipe 기능, UIButton, UISwitch에는 선택하거나, ON/OFF를 위한 Tap 기능, 확대, 축소를 위한 UIScrollView의 Pinch open / Pinch close 기능 등은 따로 구현할 필요가 없습니다.
하지만 새로운 Custom UI Control를 구현하기 위해서는 Gesture 기능도 별도로 구현해야 합니다.
Custom UI Control 구현을 위한 기본적인 클래스는 UIView class입니다.  이 클래스를 상속받으면 새로운 UI Control을 작성할 수 있는데 이때 터치 이벤트 처리를 위해 다음 method들을 overriding 하여야 합니다.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

위 method들을 보시면 Gesture를 고급 이벤트로 처리할 수 있는 것이 아니라 primitive한 터치 이벤트들을 조합하여야만 Gesture를 구성할 수 있게 되어 있습니다.

 

 

 

손가락을 대고(touchesBegan) 움직이고(touchesMoved) 떼는(touchesEnded) 행위 각각에 대하여 터치 위치 정보를 잘 계산해서 구현을 해야 합니다.
우 선 각각의 method에 넘어오는 파라미터들(touches, event)에 어떤 값들이 들어가는지 확인해 봐야 합니다. Class reference를 살펴보는 것도 한 방법이지만 명확히 알기 위해서는 샘플 프로그램으로 해당 값들을 로그로 찍어 보는 것이 가장 확실할 것입니다.
각각의 overriding된 method안에 다음과 같이 코드를 추가합니다.

 

NSLog(@”touches = %@nn events = %@”, touches, event);

 

위 함수는 c에서 printf와 동일한 기능을 해주는 함수로 객체 출력까지 지원을 해줍니다. ‘%@’가 객체 출력을 지원해 주는 포맷으로 모든 객체는 java의 toString()처럼 description이라는 메서드가 있으며, 이를 이용하여 출력이 가능하도록 해줍니다.

실제 실행시켜서 터치스크린 위에 손가락 하나를 올려놓으면 다음과 같은 로그가 찍힙니다.

 

{(
    <UITouch: 0x126c10> phase: Began tap count: 1 window: <UIWindow: 0x119940; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x119e40>> view: <PanoView: 0x118990; frame = (0 0; 320 480); layer = <CAEAGLLayer: 0x118b20>> location in window: {235, 136} previous location in window: {235, 136} location in view: {235, 136} previous location in view: {235, 136}
)}

<UITouchesEvent: 0x116390> timestamp: 151.578 touches: {(
<UITouch: 0x126c10> phase: Began tap count: 1 window: <UIWindow: 0x119940; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x119e40>> view: <PanoView: 0x118990; frame = (0 0; 320 480); layer = <CAEAGLLayer: 0x118b20>> location in window: {235, 136} previous location in window: {235, 136} location in view: {235, 136} previous location in view: {235, 136}
)}

 

 


첫 번째 touches와 두 번째 event 값들의 차이가 없어보입니다.  하지만 다음 속성을 변경하면 다른 모습을 볼 수 있습니다. UIView에서는 기본적으로 multi-touch를 인식하지 않고 multipleTouchEnabled값을 YES로 설정하여야만 multi-touch 정보를 인식할 수 있습니다.

설정 후 다시 로그를 찍어 보면 다음과 같습니다.
 

 

 

 

{(
    <UITouch: 0x126c10> phase: Began tap count: 1 window: <UIWindow: 0x119940; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x119e40>> view: <PanoView: 0x118990; frame = (0 0; 320 480); layer = <CAEAGLLayer: 0x118b20>> location in window: {235, 136} previous location in window: {235, 136} location in view: {235, 136} previous location in view: {235, 136}
)}

<UITouchesEvent: 0x116390> timestamp: 151.578 touches: {(
    <UITouch: 0x118440> phase: Stationary tap count: 1 window: <UIWindow: 0x119940; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x119e40>> view: <PanoView: 0x118990; frame = (0 0; 320 480); layer = <CAEAGLLayer: 0x118b20>> location in window: {112, 335} previous location in window: {112, 334} location in view: {112, 335} previous location in view: {112, 334},
    <UITouch: 0x126c10> phase: Began tap count: 1 window: <UIWindow: 0x119940; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x119e40>> view: <PanoView: 0x118990; frame = (0 0; 320 480); layer = <CAEAGLLayer: 0x118b20>> location in window: {235, 136} previous location in window: {235, 136} location in view: {235, 136} previous location in view: {235, 136}
)}

위의 경우는 손가락을 하나 올린 후 다시 하나를 올렸을 때 찍힌 로그 내용입니다.

Touches 와 달리 event에는 두 개의 터치 객체가 들어 있습니다.  눈 여겨 봐야 하는 부분은 phase로 하나는 staionary이고, 다른 하나는 began입니다.  Stationary는 먼저 올려 졌던 손가락이고, began은 나중에 올린 손가락입니다.  Touches는 해당 method가 호출하게 한 터치 정보에 대해서만 넘겨주고(위의 경우 두 번째 손가락 정보만 넘깁니다.) event에는 모든 정보를 담고 있습니다.  따라서 현재 스크린 내의 올려진 손가락 개수가 중요한 gesture의 경우 event 값을 사용하여야 합니다.
그리고 각각의 터치 정보에는 tap count 값과 윈도우상의 터치 위치(location in window), 이전의 윈도우상의 터치 위치(previous location in window) 값 등을 가지고 있습니다.
이런 정보들을 잘 조합해서 Gesture를 구성하여야 합니다.

Touch Gesture 구성의 유의점
Gesture 를 구성하기 위해 인터넷상에서 여러 샘플 소스를 구할 수 있는데, 방식이 조금씩 틀릴 수 있습니다.  이는 개발자가 해당 Gesture를 구현할 때 정의가 서로 달라서 발생하는 현상입니다.  간단한 Gesture는 동일할 수 있지만, 복잡하거나 여러 개의 Gesture를 동시에 인식하도록 만들 경우 구현 방식이나 경험 정도에 따라 정의가 약간씩 달라 집니다.
예 를 들면 pinch open / close의 경우 손가락 두 개의 좌표 값으로 처리하지만, 만약 3개의 손가락이 올려져 있는 경우에도 처리를 해야 할지, 3개를 올렸다가 하나를 떼더라도 나머지 두 손가락으로 처리되어야 할지에 따라 조건들이 달라질 수 있습니다.
Tap 만 인식하는 경우에는 Tap Count나 TouchesEnded 메서드에서 간단하게 인식하면 되지만 Double Tap까지 인식해야 하는 경우 같은 방법을 쓰면 두 번 터치시 Tap과 Double Tap이 동시에 불려지게 됩니다.  이 경우에는 timer를 이용하여 한번 터치 후 다시 터치가 일정 시간 내에 일어나는지를 확인해서 일정 시간 내에 터치가 일어나면 Double Tap만, 일어나지 않으면 Tap만을 호출하도록 해야 합니다.
지 도 뷰어 같이 Tap시 전체화면/일반화면 변환, Double Tap시 지도 레벨 확대, Pinch open/close시 지도 확대 / 축소, drag시 지도이동, touch & drag시 context 메뉴 등 다양한 Gesture를 인식하는 뷰를 구성하기 위해서는 Timer와 state machine을 적절히 이용해야만 가능합니다.

Touch Gesture 로직의 모듈화
경 우에 따라서는 복잡해질 수 있고, 개발자에 따라 로직이 달라질 수 있기 때문에 Gesture별로 모듈화를 시켜서 재활용하면 처음 개발은 어렵지만 차후 쉽게 Gesture를 적용할 수 있을 것이라고 생각이 드실 겁니다.  누구나 생각이 비슷하듯이 apple에서도 이 부분에 대해서 이미 생각을 해 놨습니다.  iOS4.0부터 모듈화를 지원합니다. (정확히는 iPhone OS 3.2부터 지원하지만 해당 버전은 iPad에서만 쓸 수 있기 때문에 4.0부터라고 언급한 것 입니다.)

iOS4.0부터 Gesture처리를 위해 UIGestureRecognizer class가 새로 추가되었습니다.  UIView클래스에 addGestureRecognizer: method를 이용하여 gesture를 등록하면 됩니다.
기본적으로 제공하는 gesture는 다음과 같습니다.

 

 

 

 

 Gesture

 UIKit class

 Tapping

 UITapGestureRecognizer

 Pinching in and out

 UIPinchGestureRecognizer

 Panning or dragging

 UIPanGestureRecognizer

 Swiping

 UISwipeGestureRecognizer

 Rotating

 UIRotationGestureRecognizer

 Long press

 UILongPressGestureRecognizer

 

 


편 리하게 Gesture를 View에 적용시킬 수 있습니다.  여기서 유의할 점은 모듈화 되었어도, 여러 개의 Gesture를 동시에 인식시키기 위해서는 각각의 Gesture에 대해서 이벤트를 delay시키거나 cancel시키는 작업이 필요합니다. 예를 들어 Double Tap 인식 시에는 Tap Gesture를 canceling시켜야 하는 경우입니다.  이를 위해 다양한 method들이 준비되어 있습니다.

그 리고 기본으로 제공되는 Gesture 이외의 custom으로 구성하기 위해서는 위에서 설명 드린 것과 비슷하게 방식을 사용하셔야 합니다. UIGestureRecognizer class를 상속받아서 아래 method들을 overriding 하셔서 구성하여야 합니다.

 

 

 

 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

 


물 론 Discrete Gestures, Continuous Gestures에 따라 내부 state machine도 관리해야 하고, 다양한 method들을 overriding해야 합니다. (Discrete Gestures, Continuous Gestures는 UIGestureRecognizer class를 소개할 때 나온 용어입니다.)

Touch Gesture 결론
iOS4.0 이 정식 출시되었지만, 3.X대를 쓰는 사용자들이 있을 것임으로 당분간은 UIView에서 터치 이벤트를 직접적으로 처리하는 방식으로 개발을 해야 할 것입니다.  하지만 iOS4.0에서 소개하는 UIGestureRecognizer 클래스 동작 방식을 이해하고, 모듈화 시킨다면 4.0으로 넘어갈 시에도 문제없이 변경 가능하리라 봅니다.  분량상 직접적인 구현 코드는 넣지 못했지만 정의만 명확히 한다면 구현하는데 문제 없으리라 생각됩니다.

예전에 수학선생님이 하신 말씀이 생각나네요.
“수학 문제는 정의와 원리만 정확히 알면 다 풀 수 있다.”