Development
글수 82
원본은 구글 독스에서 틈틈히 업데이트 하겠습니다.
http://bit.ly/9mzlS7
답글로 의견 주시면 원본에 반영하겠습니다.
----------
아이폰 어플 개발기 - Chapter2. Touch Event Handling
by 박주일(@KeatonY)
난이도로 보면 쉬운 내용이지만 개발 경험담을 공유해보기 위해서 정리했습니다.
잘못된 부분이나 개선해야 하는 부분들 코멘트 해주시면 뼈가되고 살이되는 양분으로 활용하겠습니다.
적어도 일주일에 한번씩 업데이트 할 계획입니다. (늦어지면 채찍질도 부탁드립니다. ^^;)
개발 공부에 많은 도움을 받고 있는 네이버 맥부기 카페와 KIDG 두곳에 올릴 계획입니다.
올리지 말라고 하시면 안올리겠습니다. ^^
-- 추가 코멘트 --
네이버 맥부기 카페는 준회원이라 강좌게시판에 쓸수가 없네요. 일단 등업부터 해야할듯 ^^;
아래 내용은 제가 개발하다 경험한 것을 위주로 적었습니다.
빠진 내용이나 잘못된 내용이 있을수 있는점 양해부탁드리며
트위터나 게시판 답글로 지적해주시면 지적해주신 분 이름도 포함해서 문서 업데이트 하겠습니다.
일 단 생각나는대로 써내려가는 글이라 정리안된점 양해바랍니다.
그래도 안쓰는것보다는 나을 것 같아서 꾹 참고 계속 써내려가 봅니다.
목차입니다.
1. Animate Image by UIImageView
2. Touch Event Handling
3. Play Sound & Music
4. Multi View Handling
5. Play Intro Movie
6. Game Logic (include RadomizeShuffleQueue)
2번째 내용 바로 들어갑니다.
별로 쓸 얘기가 많지 않지만 일단 써봅니다. ^^
이 문서는 레퍼런스 문서가 아니라 제 경험담 공유하는 거라
그냥 이렇게 한 사람도 있구나 이렇게 받아들여주심 좋겠습니다.
제가 사용한 방법보다 근사한 방법 올려주시면 그 분 성함과 개선된 방법도 올려서 문서 업데이트 하겠습니다.
제가 아는만큼만 딱 공개해서 모르는 부분 지적받아서 더 알고 싶은 마음도 있으니 지적부탁해요 ^^
2. Touch Event Handling
2.1 터치 이벤트 개요
1편에서 연속되는 이미지들을 배열에 넣어두고 순서대로 플레이하는 것을 작업했습니다.
거 기에 더해서 사용자가 버튼을 클릭하며 애니메이션이 동작하게 하고 싶어서 찾아봤습니다.
일단 터치 이벤트 처리 부분은
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
-(void)touchesCencelled:(NSSet*)touches withEvent:(UIEvent*)event
이렇게 4가지 메소드로 구성됩니다.
(touchesCancelled 쪽은 제가 직접 써보질 않아서 설명 생략합니다.
대략 외부 조건에 의해서 터치 작업이 취소된 경우라고 짐작만 하고 있습니다.
어플리케이션 실행 중 전화 온 경우가 여기에 해당될까요?
근데 그런 경우는 어플리케이션을 재구동하던데...말이죠...
그럼 어떤 경우인지 잘 이해가 ㅡㅡ; 경험해보신분이 도움좀....)
일 단 메소드 이름에서 보듯이 터치가 시작하고, 움직이고, 끝났을때 호출되는 메소드라는게 짐작이 가네요.
윈도 프로그래밍시에 자주 보던 MouseDown, MouseMove, MouseUp 과 같다고 이해했습니다.
MouseCancel 이 없었던 관계로 TouchesCancelled 쪽은 제가 잘 이해를 못한거겠죠.
다시 터치 이벤트로 돌아가서 다시 정리를 해보자면
손가락이 화면에 닿는 순간이 TouchesBegan,
화면에 댄 손가락을 움직이는 동안이 TouchesMoved,
화면에서 손가락을 떼는 순간이 TouchesEnded
겠죠....ㅎㅎ
뭐 짐작이었지만 결론은 맞습니다.
2.2. 터치했을때 원하는 동작하기
그럼 1편에서 만들었던 이미지를 클릭하면 애니메이션 동작하게 하려면 어디에다 코드를 넣어야 할까요?
뭐 손가락이 화면에 닿는 순간에 플레이 되었으면 좋겠다고 생각하면 Began,
손가락을 떼는 순간에 플레이하고 싶다면 Ended 에 넣으면 되겠네요.
뭐 자기가 원하는 순간에 동작하도록 하면 되겠죠.
근데 그렇다고 Moved 쪽에 넣으시면 화면에 댄 손가락을 움직이는 동안 계속 이벤트가 발생하니
원하는 결과가 아닐수 있겠네요.
뭐 그런 경우를 원하신다면 그렇게 하셔도 됩니다.
모든건 프로그래머 맘대로 ㅡㅡ;
일단 제가 하고 싶었던 ....터치하면 움직이는 이미지는
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
[imageMain startAnimating];
}
이 렇게 넣으면 됩니다. (전 손가락을 떼는 순간 애니메이션 동작하는 걸 선택했네요)
일단 해결...원하는 기능 개발 끝~
2.3. 터치 영역 지정하기
이 아닌게 위에 처럼 하면 일단 동작은 합니다만
화면 아무데나 클릭해도 위 코드가 동작되는 문제가 생깁니다.
아 왠지 이건 원한게 아닌데..
딱 현재 이미지를 터치했을때만 움직여야 하는거 아닌가 하는 생각이 들죠.
그러다 보니 필요한 코드가 추가됩니다.
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == imageMain)
{
[imageMain startAnimating];
}
}
아 뭔가 코드가 좀 추가되었군요.
첫줄은 뭔지 잘 이해 안가지만
[touch view] == imageMain;
이 부분은 이해갈 것 같습니다.
imageMain 에 해당되는 이미지가 클릭되었는지 확인하는 코드 같습니다.
UITouch *touch = [touches anyObject];
이 부분에 대한 설명이 뒤쪽에 다시 해야 할 것 같고....
일단 위 코드 정도면 기본적으로 원하는 기능들을 동작됩니다.
자 원하는 기능 구현했으니 다시 진짜로 끝~
----------------------------
하려는데 한가지 내용이 또 떠오르네요.
분명 위 방법대로 해서 플레이해보는데 클릭해도
원하는 동작이 제대로 진행되지 않아서 의아해하고 계실 분이 있을 것 같네요.
제가 지금 맥에서 XCode 돌려보면서 쓰고 있는게 아니라 기억을 더듬어가면서 적는거라 정확한 명칭은 생각나지 않습니다만
Interface Builder 로 가셔서 View 에 추가시켰던 UIImageView 객체를 확인해보시면
Enable Touch Interactive 뭐 이런 느낌의 메시지가 적힌 체크박스가 있을 거예요.
(정확한 명칭이 뭔지는 확인하면 수정하겠습니다.)
새로 UIImageView 를 추가시키면 이 체크박스가 기본적으로 Uncheck 상태입니다.
이거 체크해주시면 원하는대로 터치 이벤트가 처리될 것입니다.
-----------------------------
목 차 만들때에는 이정도만 쓰고 말 생각이었는데
제목을 거창하게 Touch Event Handling 이라고 해놓고
너 무 부실한 것 같아서 심히 죄송합니다.
그래서 조금만 더 추가해보겠습니다.
(딱 여기까지만 적용해도 문제없으신 분은 여기까지만 보셔도 괜찮습니다. ^^)
2.4. 좀 더 세밀한 터치 이벤트 처리
일 단 위에서
if ([touch view] == imageMain)
이 렇게 처리하던 것을 나중에 다 바꿔버렸습니다.
왜 바꿨냐부터 설명을 해보자면
버튼 이미지 하나 만들어두고 클릭하면 클릭된 이미지로 바꿨다가
손에서 떼는 순간 클릭된 버튼에 해당되는 기능을 수행하도록 하고 싶었는데..
그 동작을 구현하다보니 위 방법이 문제가 생겨서 였습니다.
(SDK 에서 기본으로 제공하는 버튼으로 처리하면 이런 부분들을 알아서 처리해주는데
커스텀 이미지를 이용한 버튼을 만들려고 하니 문제가 생기더군요.)
그 문제라는 것이 설명하기 약간 미묘한 부분이 있습니다만
일단 원래 위의 방법으로 처리했을때의 코드를 먼저 한번 살펴보죠.
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
if ([touch view] == imageButton)
{
UIImage *tmp = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"PressedButton" ofType:@"png"]];
imageButton.image = tmp;
[tmp release];
}
}
/*
(위 코드도 원래는
[imageButton setImage:[UIImage imageNamed:@"PressedButton.png"]];
이런식으로 깔끔했는데
1편에서 언급한 imageNamed 문제때문에 위 버전으로 바뀌었습니다. ㅡㅡ;)
*/
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
UIImage *tmp = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"GeneralButton" ofType:@"png"]];
imageButton.image = tmp;
[tmp release];
UITouch *touch = [touches anyObject];
if ([touch view] == imageButton)
{
[self doSomething];
}
}
-(void)doSomething
{
}
위 코드를 실행시키면 버튼에서 손이 떼어지는 순간 doSomething 부분 함수가 실행되는 걸 의도하고 만들었지요.
뭐 이미 뭐가 문제인지 아시는 분도 많겠지만
일단 제가 경험했던 걸 말씀드리자면....
보통 버튼을 클릭할 때 버튼을 클릭하고 떼는 순간이 같은 위치가 아닌 경우가 많습니다.
뭐 완전히 동일할 수는 없는거지만.... 약간 빗나가는 부분은 문제가 아니지만
버튼을 누르려고 사용자가 클릭했다가 맘이 변해서 취소하고 싶은 경우가 발생합니다.
일 반적으로 SDK에서 제공하는 버튼을 예로 들어보면 버튼을 누르는 순간에는 버튼 이미지도 바뀌고
그 버튼을 눌렀던 동작을 취소하고 싶은 경우
사용자는 버튼 영역을 벗어나서 손을 떼면 아무동작도 하지않고 버튼 이미지만 원래 이미지로 돌아가게 됩니다.
(글로 설명하기는 쉽게 이해가 어렵기도 한거라 나중에 업그레이드 할때는 이미지로 설명 추가해볼께요.)
이 런 당연한 동작을 위의 방법으로 터치 이벤트 처리한 경우에는 제대로 동작하지 않습니다.
예를 들어 나가기 버튼이 있어서 눌렀는데 그 순간 마음이 변했습니다.
버튼을 눌렀다는 사실을 취소하고 싶어져서 손을 쭈욱 이동해서 손을 뗐는데 나가기 버튼이 동작해버립니다.
아 뭔가 억울한 기분이 드는거죠.
결국 이 문제를 해결하기 위해서 찾아낸 코드가 아래와 같습니다.
if (CGRectContainsPoint([imageButton frame], [touch locationInView:self.view]))
위에 얘기했던
if ([touch view] == imageButton)
이 코드에 비해서 지나치게 복잡합니다만
새로운 코드를 사용하면 앞에서 설명한 취소가 안되는 문제에서 해결됩니다.
imageButton 이 차지하는 영역과 터치하고 있는 좌표가 포함되는지 확인하는 코드입니다.
위에서 적었던 예제 코드가 아래와 같이 바뀌게 됩니다.
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
if (CGRectContainsPoint([imageButton frame], [touch locationInView:self.view]))
{
UIImage *tmp = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"PressedButton" ofType:@"png"]];
imageButton.image = tmp;
[tmp release];
bButtonPressed = YES;
}
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
UIImage *tmp = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"GeneralButton" ofType:@"png"]];
imageButton.image = tmp;
[tmp release];
UITouch *touch = [touches anyObject];
if ((CGRectContainsPoint([imageButton frame], [touch locationInView:self.view])) && (bButtonPressed == YES))
{
[self doSomething];
}
bButtonPressed = NO;
}
-(void)doSomething
{
}
bButtonPressed 를 추가했던 이유로는 다른 곳에서 클릭했다가 버튼 이미지 영역에서 손을 떼는 순간
doSomething 부분이 실행되지 않게 하기 위해서 추가한 부분입니다.
아 터치 이벤트 처리하는 걸 간단하게만 설명하고 넘어가려고 시작했는데 너무 길어져 버렸습니다.
일단 여기까지 쓴 걸로 정리하고
챕 터2 완성은 다음 기회로 넘겨야겠습니다.
죄송합니다....ㅡㅡ;
