-
iOS Touch Event 관련 정리backup/iOS 2011. 7. 25. 18:53
❖ UITouch : 현재 뷰에 대한 터치 정보
@property(nonatomic,readonly) NSTimeInterval timestamp; : 터치가 발생한 시간을 초 단위로 나타낸다.
@property(nonatomic,readonly) UITouchPhase phase; : 현재 터치의 상태를 나타낸다.(5가지)
UITouchPhaseBegan
UITouchPhaseMoved
UITouchPhaseStationary : 터치된 상태로 정지
UITouchPhaseEnded
UITouchPhaseCancelled : 시스템이 터치에 대한 추적을 취소
@property(nonatomic,readonly) NSUInteger tapCount; : 화면에 연속적으로 터치한 횟수
@property(nonatomic,readonly,retain) UIWindow *window; : 터치가 최초로 발생한 윈도우
@property(nonatomic,readonly,retain) UIView *view; : 터치가 최초로 발생한 뷰
- (CGPoint)locationInView:(UIView *)view; : 뷰에서 터치의 위치정보를 반환, 좌표형식
- (CGPoint)previousLocationInView:(UIView *)view; : 이전 터치의 위치정보 반환
❖ UIEvent : 하나의 UIEvent객체는 하나 이상의 터치 정보를 가지고 있다. UIEvent 객체를 통해 들어온 터치들이
UITouch 객체 형태로 넘어오게 된다.
즉, 터치가 일어나면 이 터치 이벤트에 대한 내용이 UIEvent 객체로 저장되고, 이 객체는 터치 하나하나의
정보를 표현하는 UITouch 객체들을 포함한다.
여기서 UITouch 객체들은 NSSet의 형태로 저장되어 반환된다.
@property(nonatomic,readonly) NSTimeInterval timestamp; : 이벤트가 발생한 시간을 초 단위로 나타낸다.
- (NSSet *)allTouches; : 발생한 이벤트에 대한 모든 터치 객체들을 반환
- (NSSet *)touchesForWindow:(UIWindow *)window; : 주어진 윈도우에 속한 모든 터치 객체들을 반환한다.
- (NSSet *)touchesForView:(UIView *)view;
❖ UIResponder
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
❖ UIcontrolEvents
• UIControlEventTouchDown
• UIControlEventTouchDownRepeat
• UIControlEventTouchDragInside
• UIControlEventTouchDragOutside
• UIControlEventTouchDragEnter
• UIControlEventTouchDragExit
• UIControlEventTouchUpInside
• UIControlEventTouchUpOutside UIControlEventTouchCancel
❖ Multiple Touches
• UIView Property -> BOOL multipleTouchEnabled;
• UIView Property -> BOOL exclusiveTouch;
디폴트로 뷰의 exclusiveTouch 프러퍼티는 NO로 설정되어 있다.
YES로 설정하고 터치를 추적한다면, 터치를 추적하는 윈도우의 유일한 뷰가 된다. 윈도우의 다른 뷰는 이들 터치를 수신할 수 없다.
❖ Touch Event 처리 시 hit-test view 찾는 과정
- • It calls pointInside:withEvent: of self
- • If the return is NO, hitTest:withEvent: returns nil. the end of the story.
- • If the return is YES, it sends hitTest:withEvent: messages to its subviews. it starts from the top-level subview, and continues to
- other views until a subview returns a non-nil object, or all subviews receive the message.
- • If a subview returns a non-nil object in the first time, the first hitTest:withEvent: returns that object. the end of the story.
- • If no subview returns a non-nil object, the first hitTest:withEvent: returns self
❖ 이벤트 전송 제어
• 이벤트 전송 끄기.
디폴트로, 뷰는 터치 이벤트를 수신하지만, 이벤트 전송을 끄기 위해 userInteractionEnabled 프러퍼티를 NO 설정할 수 있다.
뷰 또한 숨겨져 있거나, 투명이라면 이벤트를 수신하지 않는다.
• 잠시동안 이벤트 전송 끄기.
어플리케이션은 UIApplication 메쏘드인 beginIgnoringInteractionEvents를 호출하고, 나중에 endIgnoringInteractionEvents 메쏘드를
호출할 수 있다.
beginIgnoringInteractionEvents는 어플리케이션의 터치 이벤트 메세지 수신 전체를 멈추게 하고, endIgnoringInteractionEvents는
메세지의 수신을 재개해가 위해 호출한다.
코드가 애니메이션을 수행중일 때 이벤트 전송을 꺼버리기 원할 때가 있을 것이다.
• 멀티플 터치 전송 켜기.
디폴트로 멀티 터치 시퀀스 동안의 첫 터치를 제외하고 모든 터치를 뷰는 무시한다.
뷰가 멀티플 터치를 다루기 원한다면, 뷰를 위한 멀티플 터치를 활성화시켜야만 한다.
이는 뷰의 multipleTouchEnabled 프러퍼티를 YES로 설정하거나, 관련 뷰를 위한 인스펙터를 사용한 인터페이스 빌더에 의해
프로그래밍적으로 행해질 수 있다.
• 하나의 뷰에 이벤트 전송 제한하기.
디폴트로 뷰의 exclusiveTouch 프러퍼티는 NO로 설정되어 있다.
YES로 설정하고 터치를 추적한다면, 터치를 추적하는 윈도우의 유일한 뷰가 된다. 윈도우의 다른 뷰는 이들 터치를 수신할 수 없다.
• 서브 뷰에 이벤트 전송 제한하기.
커스텀 UIView 클래스는 그 서브 뷰에 멀티 터치 이벤트 전송을 제한하기 위해 hitTest:withEvent: 를 오버라이드 할 수 있다.
❖ UIScrollView의 subView에 touch 이벤트 보내기
• 다음과 같은 구조 UIScrollView - UIView - UIButton
* UIScrollView 바로 하위에 있는 버튼들은 기본적으로 UIScrollView가 delaysContentTouches 값이 설정되어 있다면 이벤트가 먹힌다.
hitTest:point WithEvent:event (이 함수는 주어진 point와 event가 현재 View에서 동작하는가를 체크해 주는 것이다.)
UIScrollView는 hitTest를 subView에 보내지 않도록 구현이 된게 아닌가 하는 결론에 다다랐다. (아마도 스크롤 기능 때문이겠지.)
그래서, 결국, UIScrollView와 UIView(ContentView로 쓰인)의 클래스를 새로 만들어 hitTest함수를 새로 구현했다.
• UIScrollView쪽의 hitTest 함수
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"MissCheckScrollView hitTest");
UIView *result = nil;
for (UIView *child in self.subviews) {
if ([child isKindOfClass:[MissCheckView class]]) {
// 이것은 contentView를 찾기 위해서 인데, 다른 조건을 넣어도 무방하다. (pointInside 등)
if ((result = [child hitTest:point withEvent:event]) != nil)
break;
}
}
if (![self pointInside:point withEvent:event]) return nil;
// 이 스크롤 뷰에 point가 있는게 아니라면 nil 리턴. -> 요부분을 하지 않으면 스크롤 뷰 외의 다른 버튼들이 동작하지 않는다.
if (result == nil) return self;
// 하위 뷰 중 해당하는 뷰를 찾지 못했다면 자기 자신을 리턴 -> 요부분을 하지 않으면 스크롤이 동작하지 않는다.
return result; // 조건에 맞는 하위 뷰를 찾았다면 리턴
}
• UIView쪽의 hitTest 함수
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"MissCheckView hitTest %.f, %.f", point.x, point.y);
UIView *result = nil;
for (UIView *child in self.subviews) {
if ([child isKindOfClass:[UIButton class]]) { // 버튼이라면 (이부분도 적당히 조건을 주면 되겠다.)
if (CGRectContainsPoint(child.frame, point)) { // 현재 포인트가 버튼의 frame안에 위치한다면 버튼을 리턴
result = child;
break;
}
}
}
return result;
}