과정 ===== 1. 오거나이저의 repositories에서 추가(+)버튼을 누르고 Checkout 이나 Clone Repository를 고른다. 2. 파일의 패스나 URL을 입력한다. 3. "Host is reachable" 인디케이터가 녹색으로 바뀌면 Next버튼을 클릭한다. 4. Repository용 로컬 이름을 입력하고 Clone이나 Checkout으로 그것을 복사한다. 5. 위치를 고르고 선택된 Clone(또는 Checkout) 버튼을 클릭해 로컬 repository를 저장한다.
-----------------------------------------
로컬 시스템에 사본을 만들기 위해 Check out이나 clone을 수행한다.
일반적인 repository 작업의 추상화로 Xcode는 Git과 Subversion(SVN) 모두 싱글 또는 통합 GUI 및 작업수행과정의 저장소를 제공한다. 무엇을 선택하느냐에 따라 이 작업은 저장소를 check out(SVN용)하거나 clone(Git용)하며 프로젝트에 적용한다.
Xcode에서 Git 저장소를 클로닝하는 것은 로컬 시스템에 저장소를 세팅하고, 작업 공간에서 바로 쓸 수 있는 저장소로 만들어준다. 이 방법은 온라인이거나 오프라인이거나 상관없이 배포 버전 관리 및 코드 백업의 모든 권한을 관리할 수 있는 장점이 있다.
SVN 체크아웃 작업은 로컬 저장소를 만들지 않는다. 따라서 반드시 변화된 결과물을 저장할 수 있는 저장소 서버를 준비해야 한다.
SVN용으로 반드시 trunk, branches, tags 디렉토리로의 상대주소도 준비해야한다. 이렇게 하면, 저장소 오거나이저에서 새로운 저장소의 이름을 클릭해 간단히 필드를 채울 수 있다. 만약 SVN 서버가 인증을 필요로 한다면 유저명과 비번도 채워넣어야 한다.
이 비디오는 Sketch 샘플 코드 프로젝트가 Git 저장소를 클로닝 하는 과정을 보여준다.
주의 : *위 첫번째 줄의 백슬래시(\)는 다음 줄과 이어진다는 의미다. 한 줄로 쓸 땐 생략할 수 있다. 백슬래시를 쓴다면 그 뒤에는 빈칸이 없는 상태에서 RETURN키를 누른다. *다음 문자열의 /는 3개가 맞다. file:/// *한 줄로 입력할 때에는 file:/// 앞에 빈칸을 하나 넣는다. *따옴표(")를 이용하면 커멘트를 넣을 수 있다. 그러나 누구나 알아볼 수 있는 문장을 쓰도록 한다.
만약 import명령이 제대로 끝나면 다음 그림처럼 임포트 된 프로젝트 파일들이 배열될 것이다.
그림은 Sketch_svn Subversion reository를 만들고 Sketch 프로젝트를 그 안에 넣은 걸 보여준다. 제대로 되었다면 그림처럼 보일 것이다.
iPhone/iPad에서 애니메이션을 구현하는 것은 "Core Animation"이라 불리는 프레임 워크를 이용한다. Core Animation의 주요 기능으로는 다음 3개가 있다.
*UIView 애니메이션 *레이어 애니메이션 *트랜직션
UIView 애니메이션은 버튼과 뷰등의 유저 인터페이스를 움직이는 애니메이션이다. UIView를 계승하고 있는 오브젝트가 대상이다. 유저 인터페이스의 애니메이션전의 위치, 회전 각도, 투명도와 애니메이션 후의 위치, 회전 각도, 투명도를 설정하는 것으로 그 도중의 상태를 보간해 애니메이션한다. 레이어 애니메이션과 트랜직션에 대해서는 다음장에서 다룬다.
(1) 유저 인터페시의의 어파인 변환과 투명도 ========================= 유저 인터페이스에 대해 어파인 변환을 하려면 UIView클래스의 프로퍼티를 사용한다. 어파인 변환이란 도형에 대한 평행이동, 회전, 확대 축소등을 행하는 변환을 말한다.
(중략)
CGAffineTransform 클래스는 어파인 변환에 의해 어느정도, 평행 이동, 회전, 확대 축소하는가를 다루는 클래스다. 평행이동하는 CGAffineTransform 오브젝트를 생성하려면 CGAffineTransformMakeTranslation함수, 회전하는 CGAffineTransform 오브젝트를 생성하려면 CGAffineTransformMakeRotate함수, 확대축소하는 CGAffineTransform 오브젝트를 생성하려면 CGAffineTransformMakeScale함수를 이용한다.
(중략)
회전 후에 확대 축소를하는 등, 복수의 어파인 변환을 합성하고 싶을 떄는 다음 CGAffineTransformTranslate함수, CGAffineTransformRotate함수, CGAffineTransformScale함수를 이용한다.
(중략)
유저 인터페이스의 투명도를 지정하려면 UIView클래스의 alpha 프로퍼티를 사용한다. 10.이 완전히 보이는 상태, 0.0이 완전히 보이지 않는 상태다. 0.0일때는 터치 이벤트도 발생하지 않는다. 뷰가 가지고 있는 서브 뷰에 대해서도 투명도는 반영된다. 뷰의 색만을 투과하고 싶으면 backgroundColor프로퍼티에서 조정한다.
(중략)
이 프로그램에서는 애니메이션 1의 애니메이션 전 상태를 (0.5, 0.5) 축소, 투명도 0.8로 한다.
(중략)
또, 애니메이션 2의 애니메이션 전 상태를 180도 회전하고 (0.5, 0.5) 축소, 투명도 0.8로 한다.
(중략)
이하의 계산으로 도를 라디안으로 변환해 대입한다. M_PI는 파이의 값을 나타내는 상수다.
라디안=도*(M_PI/180)
거기에 애니메이션 3의 애니메이션 ㅓㄴ에 상태를 (0,400) 평행이동, 투명도 0.8)로 한다.
(중략)
(2)UIView애니메이션 설정 개시 =================== UIView 애니메이션을 행하려면 유저인터페이스의 애니메이션 전 위치, 회전 각도, 투명도를 지정한 다음 UIView클래스의 beginAnimations:contex:메소드를 부른다.
(중략)
애니메이션 ID와 파라메터는 애니메이션 개시 시에 불리는 메소드와 애니메이션완료 후 불리는 메소드에 건네지는 인수다. 통지 타겟의 setAnimationRepeatCount:메소드에서는 이것들의 정보를 기초로 어떤 처리를 할 것인지 정한다. 필요 없을 땐 nil을 지정한다. UIView클래스의 setAnimationDuration:메소드에서 애니메이션 시간, setAnimationCurve:메소드에서 애니메이션 커브, setAnimationRepeatCount:메소드에서 리피트 횟수, setAnimationRepeatAutoreversed:메소드에서 역방향의 애니메이션을 할지 말지를 지정한다. 애니메이션 커브에서는 점점 빠르게나 점점 느리게를 설정할 수 있다.
(중략)
애니메이션 커브에서는 이하의 상수를 지정한다.
(중략)
(중략)
(3)UIView애니메이션 델리게이트 ==================== UIView 애니메이션 델리게이트를 지정하려면 UIView클래스의 setAnimationDelegate:메소드를 사용한다.
(중략)
setAnimationWillStartSelector:메소드에서 UIView애니메이션 개시시ㅇ 불리는 메소드를 지정한다.
(중략)
통지 타겟의 메소드의 서식은 다음과 같다. 인수의 형과 순서가 같은 메소드라면 메소드명은 자유롭게 지정할 수 있다.
(중략)
이 프로그램에서는 ViewAnimationEx오브젝트 자신의 someAnimationWillStart:메소드를 통지 타겟으로 지정했다.
(중략)
통지타겟의 메소드의 서식은 다음과 같다. 인수의 형과 순서가 같은 메소드라면 메소드명은 자유롭게 지정할 수 있다.
(중략)
이프로그램에서는 ViewAnimation오브젝트 자신의 someAnimationDidSto:finished:context:메소드를 통지 타겟으로 지정했다.
(중략)
(4)애니메이션 후의 위치, 회전각도, 투명도 ======================= UIView클래스의 beginAnimations:context:메소드와 commitAnimations메소드의 사이에 애니메이션 후 위치, 회전각도, 투명도를 지정한다. 위치, 회전각도, 투명도의 지정은 commitAnimation 메소드가 불릴때까지 반영되지 않는다.
(5)UIView애니메이션 실행 ================ UIView애니메이션을 실행하려면 commitAnimations메소드를 호출한다.
(중략)
(6)프레임 지정에 의한 애니메이션 =================== 어파인 변환이 아니라 프레임에 의한 (X좌표, Y좌표, 폭, 높이)의 지정으로 애니메이션 전의 위치와 애니메이션 후의 위치를 지정할 수도 있다.
============ 읽어 올 타겟의 패스로 리소스의 URL을 생성할 때는 NSBundle 클래스의 mainBundle 메소드에서 NSBundle 오브젝트를 취득해 pathForResource:ofType:메소드에서 패스를 생성해 이용한다.
(중략)
pathForResource:ofType:메소드의 인수에는 파일명이 "bgm.wav"의 때는 파일명에 "bgm", 확장자에 "wav" 또는 파일명에 "bgm.wav"와 확장자에 ""를 지정한다. 마지막으로 NSURL클래스의 fileURLWithPath:메소드에서 패스를 URL로 변환한다.
(중략)
(2) 오디오 플래이어 생성 ============== 사운드의 재생을 할 오디오 플레이어를 생성할 때는 AVAudioPlayer클래스를 사용한다 alloc으로 메모리를 확보후 initWihtContentsOfURL:error:메소드로 생성한다.
(중략)
==== [컬럼] 포인터의 포인터 ==== 포인터의 어드레스를 한번 더 포인터에 격납하는 것이 가능하며 이 포인터를 "포인터의 포인터"라 부른다. 포인터의 포인터를 선언할 때는 다음과 같은 서식으로 선언한다.
형** 변수명;
거기에 포인터의 포인터의 포인터를 만드는 것도 가능하나 통상 사용하진 않는다. 포인터의 포인터의 잇점은 참조형의 반환값을 함수의 반환값이 아닌 인수경유로 취득할 수 있다는 것이다.
AVAudioPlayer클래스의 initWithContentsOfURL:error:메소드의 인수 error도 에러 정보를 포인터의 포인터"(NSError**)"로 취득할 수 있다. NSError오브젝트를 선언 후 그 변수의 포인터의 포인터를 대응하는 메소드의 인수에 넘긴다. 임의의 변수의 포인터는 "&변수명"으로 나타낸다. 이것으로 에러시에는 error오브젝트에 NSError오브젝트를 대입되어 돌아온다. initWithContentsOfURL:error:메소드의 에러 정보를 콘솔에 보내는 프로그램은 다음과 같다.
(중략) ---- ㅁ
(3) BGM의 재생과 정지 ============= 오디오 플레어가 사운드를 재생중인가 어떤가 살펴보려면 AVAudioPlayer클래스의 playing프로퍼티를 사용한다. 이번에는 정지 중일 때는 사운드를 재생하고, 재생중일 때는 사운드를 정지한다. 사운드를 재생하려면 AVAudioPlayer클래스의 numberOfLoops프로퍼티에서 루프 회수를 지정해 currentTime프로퍼티에서 재생위츠를 지정하고 play메소드로 재생을 개시한다. 사운드를 정지하려면 stop메소드를 부른다.
(중략)
(4)SE 재생 ======= SE재생은 정지중일 때는 재생하고, 재생중일 때는 재생위치를 처음으로 돌린다.
(5) 오디오 플레이어의 볼륨 조작 =================== 오디오 플레이어의 볼륨을 조작하려면 AVAuidoPlayer 클래스의 volume 프로퍼티를 사용한다. 오디오 플레이어의 볼륨의 최최값은 1.0으로 사운드 파일당의 볼륨의 조정에 이용한다. 단말의 볼ㄹㅁ 조작에 대해서는 다음장에서 다룬다.
6-3-1 가속도와 단말의 방향 취득 프로그램 구성 ==========================
단말 내장의 가속도 센서를 이용해서 가속도와 단말이 향한 방향을 취득해 표시하는 프로그램을 만든다. 표시하는 정보는 다음과 같다. 가속도 센서는 모든 iOS단말에 이용 가능하다.
X축 가속도 (m/s) Y축 가속도 (m/s) Z축 가속도 (m/s)
단말 화면이 위를 향해 정지 한 상태에서는 X축 가속도와 Y축 가속도가 0.0, Z축 가속도가 -9.8이 된다. 이것은 지면을 향한 중력(9.8m/s)가 작용하기 때문이다.
(중략)
(1) 가속도 통지 개시 ============ 가속도의 통지를 개시하려면 UIAccelerometer 클래스를 이용한다. UIAccelerometer 클래스의 sharedAccelerometer클래스에서 UIAccelerometer 오브젝트를 취득한다.
(중략)
UIAccelerometer 클래스의 주요 프로퍼티는 다음과 같다. 이번에는 0.1초 마다 AccelerometerEx 클래스 자신에 가속도를 통지하도록 지정한다.
(중략)
(2) 가속도 센서 델리게이트 =============== 가속도 센서의 이벤트 통지 타겟에는 UIAccelerometerDelegate 프로토콜을 구현한 오브젝트를 지정한다. UIAccelerometerDelegate 프로토콜이 가진 메소드는 다음과 같다.
(중략)
가속도는 UIAcceleration 형의 값으로 UIAcceleration 클래스는 다음 4개의 프로퍼티를 가진다. UIAccelerationValue형과 NSTimeInterval형은 double형이다.
(중략)
(3) 가속도에 로우패스 필터 걸기 ================== acceleration.x, acceleration.y, acceleration.z 가 가속도 센서로부터 취득한 가속도의 값이다. 이 값을 그대로 이용해도 좋으나 그대로는 가속도 센서의 순간적인 데이터 변화의 영향을 받아 수치가 안정되어있지 않으므로, 일반적으로 로우패스필터를 통해 안정된 값을 이용한다. 구체적으로는 다음과 같은 "현재의 가속도*0.1+전회의 가속도*0.9"를 계산해 전회의 가속도로부터 스무드하게 현재의 가속도에 가까워지도록 한다.
(중략)
단말 흔들기등 순간적인 움직임을 계산하고 싶은 경우는 현재의 가속도로부터 로우패스 필터의 값을 뺀 값을 이용한다.
(중략)
(4)단말이 향한 방향의 통지 개시 ================== 단말 회전시에 향한 방향을 취득하려면 UIDevice클래스와 NSNotificationCenter 클래스를 사용한다. UIDevice 크래스의 currentDevice메소드에서 UIDevice 오브젝트를 취득해 beginGeneratingDeviceOrientationNotification 메소드로 단말이 향한 방향의 통지를 시작한다.
(중략)
다음, NSNotificationCenter 클래스의 defaultCenter 메소드에서 NSNotificationCenter 오브젝트를 취득해 addObserver:selector:name:object:메소드로 단말이 향한 방향의 통지 타겟 오브젝트와 메소드를 지정한다.
(중략)
SEL은 메소드의 참조를 유지하는 형으로 "@selector(메소드명)"으로 통지 타겟의 메소드 명을 지정한다. 통지 타겟의 메소드 서식은 다음과 같다. 인수의 형과 순서가 같은 메소드라면 메소드 명은 자유롭게 지정할 수 있다.
(중략)
이 프로그램은 AcceleromaterEx 오브젝트 자신의 didRotate:메소드를 통지 타겟으로 지정하고 있다.
selector:@selector(didRotate:)
통지의 종류에는 단말이 향한 방향 변경의 통지를 의미하는 "UIDeviceOrientationDidChangeNotification"을 지정한다.
(5) 단말이 향한 방향 취득 ============== didRotate:메소드에 통지된 NSNotification 오브젝트로부터 단말이 향한 방향을 취득한다. NSNotification 클래스의 object 메소드로 통지의 발신원의 UIDevice 오브젝트를 취득해 orientation 프로퍼티로 단말의 방향을 받아들인다.
(중략)
단말이 향한 방향에는 다음과 같은 상수가 대입되어있다.
(중략)
UIViewController 클래스의 shouldAutorotateToInterfaceOrientation:메소드도 단말 회전시에 불리지만 이것은 화면이 회전하기 전에 불리는 메소드가 된다.
(1) 로케이션 매니저 생성 ============== 위치정보 또는 방위정보를 취득하려면 우선 처음에 CLLocationManager 오브젝트를 생성한다. alloc으로 메모리를 확보후 init 메소드로 생성한다. 주요 프로퍼티는 다음과 같다.
(중략)
(2) 위치 정보 통지의 개시와 정지 ================== 위치정보 통지가 가능한지 어떤지 조사하려면 CLLocationManager 오브젝트의 locationServicesEnabled 프로퍼티를 사용한다.
(중략)
위치 정보의 통지를 개시하려면 startUpdatingLocation 메소드를 사용한다.
(중략)
위치 정보 통지를 정지하려면 stopUpdatingLocation 메소드를 사용한다.
(중략)
(3) 방위 정보 취득의 개시와 정지 =================== 방위 정보 통지가 가능한가 어떤가 조사하려면 CLLocationManager 오브젝트의 headingAvailable 메소드를 사용한다.
(중략)
방위 정보의 통지를 개시하려면 startUpdatingHeading메소드를 사용한다. (중략)
방위 정보의 통지를 정지하려면 stopUpdatingHeading 메소드를 사용한다.
(중략)
(4) 로케이션 매니저의 델리게이트 =================== 로케이션 매니저의 이벤트 통지 타겟은 CLLocationManagerDelegate 프로토콜을 구현한 오브젝트를 지정한다. CLLocationManagerDelegate프로토콜을 가진 메소드는 다음과 같다.
(중략)
CLLocation 오브젝트의 coordinate프로퍼티에서 CLLocationCoordinate2D형의 값을 취득한다. CLLocationCoordinate2D형의 구조체는 위도latitude와 경도longitude의 값을 유지한다. CLLocationDegrees형은 double형과 같다.
(중략)
단말 설정으로 위치정보의 취득이 불가능한 경우는 NSError 오브젝트의 code프로퍼티에 kCLErrorDenied가 들어간다.
(중략)
CLHeading 오브젝트의 trueHeading 프로퍼티가 진북의 방위를 나타내는 값(0.0~360.0)이다. CLLocationDerection형은 double과 같다.
======== 컬럼 : 진북과 자북 ======== 진북은 지구 지축의 북, 자북은 방위 자석이 가리키는 북이다. 진북과 자북은 장소에 따라 어긋날 수 있다. 이 어긋남을 자기편각이라 부르고 동경에서는 약 7도 어긋난다. 진북이 아니라 자북의 방위를 알려면 magneticHeading 프로퍼티를 사용한다.
CLLocationDerection magneticHeading : 자북을 나타내는 값(0.0~360.0)
(1)카메라와 포토 앨범의 이용가능 체크 =================================== 카메라와 포토앨범에 액세스하기 전에 이용가능한가 어떤가를 체크한다. 체크하려면 UIImagePickerController클래스의 isSourceTypeAvailable:메소드를 사용한다. 또 포토 앨범은 현죠ㅗㄴ하는 모든 단말에서 이용가능하지만 카메라는 단말에 따라 다르다.
(중략)
소스 타입에는 다음 상수를 지정한다.
(중략)
(2) 이미지 피커의 생성 ===================== 카메라 또는 포토 앨범으로 부터 화상을 읽어들이려면 UIIMagePickerCOntroller클래스를 사용한다. alloc으로 메모리를 확보후 init메소드로 생성한다. 주요 프로퍼티는 다음과 같다. 소스 타입은 표 6-1-1과 같다.
(중략)
델리게이트 형의 [id<UINavigationControllerDelegate, UIImagePickerControllerDelegate>]는 델리게이트로 지정한 오브젝트가 UINavigationControllerDelegate 프로토콜과 UIImagePickerControllerDelegate 프로토콜의 양방을 구현할 필요가 있음을 나타낸다. 구현 해야할 프로토콜이 2개 존재하는 것에는 이미지 피커가 네비게이션을 계승하고 있기 때문이다.
(3) 내비게이션의 델리게이트 =========================== 내비게이션의 이벤트 통지 타겟에는 UINavigationControllerDelegate 프로토콜을 구현한 오브젝트를 지정한다. UINaviationControllerDelegate 프로토콜을 가진 메소드는 다음과 같다.
(중략)
이번은 UINavigationControllerDelegate 프로토콜의 구현 선언만 한다. UINavigationControllerDelegate 프로토콜의 메소드는 모든 옵션 메소드로 이번에는 처리할 필요는 없으므로 메소드를 구현하진 않고 있다.
(4) 이미지 피커의 델리게이트 ============================ 이미지 피커의 이벤트 통지타겟에는 UIImagePickerControllerDelegate 프로토콜을 구현한 오브젝트를 지정한다. UIImagePickerControllerDelegate 프로토콜을 가진 메소드는 다음과 같다.
(중략)
미디어 정보로부터 이미지를 취득하려면 키[UIImagePickerControllerOriginalImage]의 값을 취득한다.
(1) GET에 의한 HTTP통신 ======================== HTTP통신을 하려면 처음 NSURLRequest오브젝트를 생성한다. 타임아웃(디폴트 4분)을 설정하지 않은 때는 RequestWithURL:메소드, 타임아웃을 설정한 때는 requestWithURL:cachePolicy:timeoutInterval:메소드를 사용한다.
다음으로 NSURLConnection클래스의 connectionWithRequest:delegate:메소드로 HTTP통신을 개시한다.
(중략) 여기서 지정한 델리게이트에 대해서 수신 데이터와 통신 결과의 통지를 한다. 이 프로그램에서는 HttpEx 오브젝트 자신을 델리게이트로 지정한다.
(2) NSURLConnection 델리게이트 ================================ NSURLConnection 델리게이트를 가진 메소드는 다음과 같다.
(중략)
이 프로그램에서는 HTTP통신의 개시시에 NSMutableData오브젝트를 생성해, 데이터 수신시에 NSMutableData 오브젝트에 데이터를 추가해 HTTP통신의 성공시에 수신 데이터를 문자열로 변환해 텍스트 필드에 대입한다. HTTP통신 실패시에는 "통신에러" 문자열을 텍스트 필드에 대입한다.
(3) 가변 길이 바이트 배열 ======================= NSMutableData클래스는 길이가 가변하는 바이트 배열 클래스다. 생성시에는 data메소드를 사용한다.
(중략)
바이트 배열을 추가할 때는 appendData:메소드를 사용한다.
(중략)
(4) 인디케이터 ============== 현재 통신중인 것을 나타내기 위해 빙글빙글 도는 인디케이터를 표시한다. 4장[4-6 WEB뷰]에서는 타이틀 바에 표시했지만 이번에는 뷰 상에 배치한다.
(그림 생략)
인디케이터를 생성하려면 UIActivityIndicatorView 클래스를 사용한다. alloc으로 메모리 확부 후 init메소드로 생성한다. 인디케이터의 주요 프로퍼티는 다음과 같다.
(중략)
인디케이터 스타일에는 다음과 같은 상수를 지정한다. 이번에는 희고 큼을 지정했다.
(중략)
hidesWhenStopped 프로퍼티에 YES를 대입하는 것으로 인디케이터의 애니메이션 정지시에 표시하지 않게 된다. 인디케이터의 애니메이션을 개시시키려면 startAnimating메소드, 정지시키려면 stopAnimating 메소드를 사용한다.
(후략)
============================== [컬럼] POST에 의한 HTTP 통신 ============================== POST에 의한 HTTP통신을 하려면 setHTTPMethod:메소드에서 "POST"를 지정해 setHTTPBody:메소드에 업로드 하는 송신데이터를 지정한다.
(1) 내비게이션 컨트롤러 생성 =========================== 내비게이션 컨트롤러를 생성하려면 UINavigationController클래스를 사용한다. alloc으로 메모리 확보후 init메소드로 생성한다. 내비게이션의 1개 아래 계층으로 이동하는 것에는 pushViewController:animated:메소드를 사용한다.
(중략)
(2) 파일과 폴더 계층의 정의 ========================= NavigationEx클래스를 init메소드로 생성한 때 파일과 폴더 계층의 정의를 한다. 파일은 NSString형, 폴더는 NSDictionary형을 사용한다.
종류 데이터 형 내용 파일 NSString형 값은 파일명 폴더 NSDictionary형 키는 파일명, 값은 폴더에 포함된 파일과 폴더의 배열 표 4-8-1 파일과 폴더의 계층 정의
다음 그림과 같은 파일과 폴더의 계층을 정의하려면 다음과 같이 기술한다. 파일과 폴더의 계층을 정의 하면 마지막에 테이블에 표시할 폴더를 _folder 변수에 대입한다.
(중략)
(3) 내비게이션 바의 타이틀 지정 ============================== 내비게이션 바의 타이틀을 지정하려면 UIViewController클래스의 title 프로퍼티에서 지정한다. 이번에는 _folder 변수의 키를 지정한다.
(중략)
(4) 내비게이션 바의 버튼의 추가 내비게이션 바의 버튼을 생성하려면 UIBarButtonItem클래스를 사용한다. alloc으로 메모리를 확보후 init메소드로 생성한다.
(중략)
스타일에는 다음 상수가 지정된다. 통지 타겟의 오브젝트와 메소드의 지정은 UIButtuon클래스와 같다. (중략)
SEL은 메소드의 참조를 가지는 형으로 [@selector(메소드명)]으로 통지 타겟의 메소드명을 지정한다. 통지 타겟의 메소드의 서식은 다음과 같다. 인수의 형과 순서가 같은 메소드라면 ㅔㅁ소드명은 자유롭게 지정할 수 있다.
(중략)
이 프로그램에서는 NavigationEx 오브젝트 자신을 clickButton:메소드를 통시 타겟으로 지정하고 있다.
action:@selector(clickButton:)
(5)1단 아래 계층으로 이동 ======================== 1단 아래 계층으로 이동하려면 애플리케이션 델리게이트에서도 이용한 pushViewController:animated:메소드를 사요한다. 새로운 NavigationEx오브젝트를 생성해, level과 page를 저정해 이동한다.
아래 계층에는 내비게이션 바의 왼쪽에 윗단 계층으로 돌아가는 버튼이 표시된다. 이것 이외의 방법으로 위의 계층으로 돌아가려면 popViewControllerAnmated:메소드를 사용한다.
(중략)
가장 위의 계층까지 돌아가려면 popToRootViewControllerAnmated:메소드를 사용한다.
(중략)
(6)테이블 뷰의 생성 ================== 테이블 뷰를 생성하려면 UITableView클래스를 사용한다. alloc으로 메모리를 확보하고 init메소드로 생성한다. 주요 프로퍼티는 다음과 같다.
(중략)
(7) 테이블 뷰의 델리게이트 ========================= 테이블 뷰의 이벤트 통지 타겟에는 UITableViewDelegate 프로토콜과 UITableViewDataSource 프로토콜을 구현한 오브젝트 를 지정한다. UITableViewDelegate 프로토콜에서는 테이블의 표시 관련의 처리를 하고 UITableViewDataSource프로토콜에서는 테이블의 데이터 관련 처리를 한다. UITableViewDelegate프로토콜이 가진 주요 메소드는 다음과 같다
(중략)
액세서리 버튼은 상세 버튼등의 셀에 추가 하는 보조 버튼이다. 이 프로그램에서는 셀 높이로 50을 반환해 셀또는 액세서리 버튼을 클리한 때 0계층째은 1게층 아래로 이동하고 1계층 때는 얼럿을 표시하고 있다. UITableViewDataSource프로토콜이 가진 주요 메소드는 다음과 같다. 이번에는 셀수로서 _itmes.count를 반환한다.
(중략)
(8) 테이블의 셀 생성 ================== 테이블의 셀을 만드려면 UITableViewCell클래스를 사용한다. alloc으로 메모리를 확보하고 init메소드로 생성한다. 내비게이션의 1ㄱ단 아래 계층으로 이동하려면 pushViewController:aniated:메소드를 사용한다.
(중략)
스타일에는 다음의 상수를 지정한다. 화면과 라벨의 표시위치가 바뀐다.
(중략) reuseIdentifier에는 임의 재이용 ID를 지정한다. 재이용 ID를 지정해 생성하면 다음번 UTableView클래스의 dequeueReusableCellWithIdentifier:메소드에서 같은 셀을 취득할 수 있게 된다.
(중략)
셀에 표시하는 텍스트를 편집하려면 UITableViewCell클래스의 texLabel 프로퍼티를 편집한다. 상세 텍스트를 지정하려면 detailTextLable프로퍼티, 이미지를 지정하려면 imageView프로퍼티를 편집한다.
또 자유로이 컴포넌트를 배치하고 싶으면 contetView프로퍼티와 backgroundView프로퍼티를 편지합니다. 셀에 액세서리를 추가하려면 다음 프로퍼티를 사용한다.
(중략)
액세서리 타입은 다음 상수를 지정한다.
(중략)
이 프로그램에서는 텍스트에 파일명 또는 폴더명을 지정해 폴더 시는 액세서리로 상세 버튼을 추가한다.
(9) 테이블의 셀 선택 해제
테이블 셀의 선택 해제를 하는 것은 UTableView클래스의 deselectRowAtIndexParh:anmated 메소드를 사용한다.
(중략)
셀의 위치에는 UITableView 클래스의 indexPathForSeectedROw메소드에서 취득한 현재 선택중의 셀 위치를 지정한다 (후략)
================================================= [컬럼] 경도와 위도 '위도'(latitue)라는 것은 적도를 기점(0도)으로 해서 북으로 +90도(북위) 남으로 -90(남위)의 각도를 표시하는 것, '경도'(longitude)라는 건 영국의 구 그리니치 천문대를 지나는 자오선을 기점(0도)로 해서 동으로 180도(동경), 서로 180도(서경)의 각도를 표시하는 것이다 =================================================
(3) 줌 값의 준비 =============== 줌 값은 MKCoordinateSpan형 값으로 유지한다. latiudeDelta에 지도상에 표시되는 남북의 거리, longitudeDelta에 지도상에 표시되는 동서의 거리를 지정한다. 이번엔 0.01을 지정
type struct { CLLocationDegrees latitudeDelta; CLLocationDegrees longitudeDelta; } MKCoordinateSpan;
(4) 줌과 위치의 지정 =================== 맵뷰에 줌과 위치 값을 지정하는 것은 우선 MKCoordinateRegion형의 변수에 줌과 위치값을 대입한다. center는 위치, span은 줌값의 값이다.
(1)Web뷰 생성 ============= Web뷰를 생성하려면 UIWebView클래스를 사용한다 alloc으로 메모리를 확보하고 init메소드로 생성한다. web뷰의 주요한 프로퍼티는 다음과 같다.
BOOL scalePageToFit Web페이지의 스케일을 피트시킬까 말까
(2) 뷰 사이즈의 자동조정 ======================= UIWebVIew클래스에는 뷰 사이즈를 자동조절하는 autoresizeingMask프로퍼티가 있다. 이 프로퍼티를 지정하는 것으로 부모의 VIew의 화면 사이즈 변화에 맞춰 자신의 View의 화면 사이즈도 리사이즈하도록 된다.
(중략)
자동 스케일 조정에는 다음의 상수를 |로 연결해 지정한다.
(중략)
(3)인디케이터의 표시/비표시 ========================== 현재 통신중인 것을 표시하기 위해 타이틀바에 빙글빙글 돌아가는 인디케이터를 표시한다.
(11)WEB뷰는 메모리 해방전에 델리게이트 해제 WEB뷰의 메모리를 해방할 때는 delegate프로퍼티를 nil로 하고 release한다. 통신 처리중에 델리게이트를 지정한 채 release처리를 하면 release후도 처리를 하려는 문제가 발생할 가능성이 있다.
_webView.delegate=nil; [_webView release];
-----------------------------------
컬럼 : 리소스로부터 HTML 읽어오기
리소스로부터 HTML을 읽어오기는 다음과 같이 기술한다. 인수로는 UIWebView오브젝트와 HTML리소소스 파일명을 지정한다.
(중략)
UIWebVIew클래스의 loadHTMLString:baseURL:메소드로 HTML문자열과 베이스 URL로부터 HTML을 읽어온다.
(중략)
파일로부터 읽어오는 것은 다음 장에서 설명한다.
============================================== 컬럼 : JavaScript의 실행 WEB뷰에서 읽어들인 HTML의 JavaScript의 함수를 구현하려면 UIWebVIew클래스의 stringByEvaluatingJavaScriptFromString:메소드를 사용한다. HTML 읽어오기를 완료하지 않으면 실행할 수 없으므로 webViewDidFinishLoad:webView:메소드가 불려진 뒤 이용하라
(중략)
얼럿 표시하는 JavaScrip를 실행하면 다음과 같이 기술한다. [_webView string ByEvaluationJavaScriptFromString:@"alert('Hello!')"];
(1)텍스트 필드의 생성 =================== 텍스트필드를 생성하려면 UITextField클래스를 사용한다. alloc으로 메모리를 확보후, init메소드로 생성한다. 주요 프로퍼티는 다음과 같다. 또, frame 프로퍼티와 backgroundColor프로퍼티는 UITextFiled클래스의 부모 클래스에 있는 UIView클래스에서 정의되어있다.
(중략)
textAlignment 프로퍼티는 다음과 같이 상수를 지정한다. (중략)
borderSytle 프로퍼티의 보더 스타일에는 다음과 같이 상수를 지정한다. (중략)
UITextField는 UITextInputTraits프로토콜을 실장하고 있다.
UITextInputTraits프로토콜 프로퍼티 의미 UIKeyboardApperance keyboardAppearance 키보드 어피어런스(표 4-3-3 참조) UIKeyboardType keyboardType 키보드 종류 (표 4-3-4참조) UIkeyboardType returnKeyType 리턴키의 종류 (표 4-3-5참조)
keyboardAppearance 프로퍼티의 키보드 어피어런스에는 다음과 같은 상수를 지정한다.
(중략)
keyboardType 프로퍼티의 키보드 종류에는 다음과 같은 상수를 지정한다. 디폴트 ASCII 숫자와 기호 URL 숫자 전화 이름 메일 알파벳 표 4-3-4 키보드 종류의 상수
returnKeyType 프로퍼티의 리턴키 종류에는 다음의 상수를 지정한다. 리턴키의 종류의 설정에 따라 변하는 것은 소프트웨어 키보드의 결정 버튼에 표시되는 문자열뿐이다. 구체적인 기능은 스스로 구현할 필요가 있다.
디폴트 Go Google 접속 다음 루트 검색 송신 Yahoo Done 비상 전화 표 4-3-5 리턴키 종류의 상수
(2) 텍스트 필드의 델리게이트 =========================== 텍스트 필드의 이벤트 통지 타겟에는 UITextFieldDelegate 프로토콜을 구현한 오브젝트를 지정한다. UITextFieldDelegate 프로토콜을 가진 메소드는 다음과 같다.
=편집관리
(중략)
=텍스트 변경
(중략)
이번에는 리턴 버튼을 누를 때 소프트웨어 키보드를 닫는 처리와 텍스트 변경시에 140문자제한을 하는 처리를 하고 있다.
(3)소프트웨어 키보드의 표시/비표시 ================================= 소프트웨어 키보드를 닫으려면 UITextField 오브젝트의 resignFirstResponder메소드를 불러 퍼스트 리스폰더를 해제한다. 컴포넌트 중에서도 가장 먼저 메소드가 보내지는 컴포넌트를 "퍼스트 리스폰더"라 부른다. 퍼스트 리스폰더는 통상 직전까지 선택된 컴포넌트다. 텍스트 입력 가능한 컴포넌트가 퍼스트 리스폰더가 되면 키보드가 열리고 해제되면 키보드가 닫힌다.
(중략)
소프트웨어 키보드를 닫으려면 becomeFirstResponder메소드를 불러 퍼스트 리스폰더를 지정한다.
텍스트 표시만의 둥근사각형 버튼을 생성하려면 UIButton클래스의 buttonWithType메소드를 사용한다.
(중략)
이번에는 UIButtonTypeRoundedRect를 지정해서 둥근 사각형 버튼을 만들고, setTitle:forState:메소드로 표시하는 텍스트를 지정한다.
(중략)
프로토콜의 상태는 다음과 같은 상수를 지정한다.
컨트롤 상태의 상수 의미 UIControlStateNormal 보통 때 UIControlStateHighlighted 하이라이트 때 UIControlStateDisabled 유효하지 않은 때 UIControlStateSelected 선택시 표4-2-2 컨트롤 상태에 지정하는 상수
또, 버튼을 누른 때에 어느 버튼이 눌렸는가를 판정하기 위해서 각버튼에 태그라 불리는 ID를 지정한다. 이벤트 통지 타겟에서는 이 태그를 보고 어떤 처리를 할 것인지 결정한다.
(중략)
(2)이벤트 리스너 지정 ============
컴포넌트로의 이벤트 통지 타겟의 지정은 UIControl 클래스의 addTarget:action:forControlEvents:메소드를 사용한다. UIButton등의 이벤트 통지를 하는 컴포넌트는 UIControl을 상속하고 있다.
(중략)
SEL 은 메소드의 참조를 유지하는 형으로 "@selector(메소드명)"으로 통지 타겟의 메소드명을 지정한다. 통지 타겟의 메소드의 서식은 다음과 같다. 인수 형과 순서가 같은 메소드라면 메소드명은 자유로이 지정할 수 있다.
(중략)
여기에서는 ButtonEx 오브젝트 자신의 clickButton:메소드를 통지 타겟으로 지정한다.
action:@selector(clickButton:)
컨트롤 이벤트는 다음의 상수를 지정한다. 통상 터치 업 시에 처리를 하므로 UIControlEventTouchUpInside를 지정한다.
(중략)
(3) 이미지 버튼의 생성 ============= 이미지 버튼을 성성하려면 버튼 종별에 UIButtonTypeCustom을 지정하고 표 4-2-2의 "컨트롤 상태에 지정하는 상수"에 이미지를 지정한다. 컨트롤의 상태에 지정하는 상수는 setTitle:forState:메소드와 같다.
(중략)
(4) 얼랏과 YES/NO다이얼로그 표시 얼럿을 표시하려면 UIAlertView클래스를 사용한다. alloc으로 메모리를 확보하고 intWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:메소드로 오브젝트를 생성하고 show메소드로 화면에 표시한다.
(중략)
"델리게이트"는 이벤트의 통지 타겟이다. 델리게이트에는 "권한을 위임한다"라는 의미가 있다. 얼럿과 다이얼로그에서는 버튼을 누를 때 하는 처리 등을 위임할 수 있다.
프로토콜의 메소드에는 필수 메소드의 경우에는 반드시 구현할 필요가 있다. UIAlertViewDelegate 프로토콜의 메소드는 모든 옵션 메소드이므로 이 중에 필요한 것만 구현한다. 이 프로그램에서는 alertView:didDismissWithButtonIndex:메소드를 구현했다.
(중략)
이번 얼럿 뷰의 버튼이 눌릴 때 눌린 버튼의 INDEX 를 표시하도록 했다.
(중략)
(6)액션 시트의 표시 ============ 액션 시트를 표시하려면 UIActionSheet 를래스를 사용한다. alloc으로 메모리를 확보 후 initWithTitle:delegate:cancelButtonTitle:destructiveButtonTitle:otherTitles:메소드로 오브젝트를 생성하고 ShowInView:메소드로 화면을 표시한다.
(중략)
(중략)
액션 시트에서도 버튼을 눌렀을 때 통지 타겟이 되는 델리게이트를 지정한다. UIActionSheetDelegate 프로토콜의 actionSheet:didDismissWithButtonIndex 메소드를 구현한다.
(중략)
이번 액션 시트의 버튼을 눌렀을 때 눌린 버튼의 INDEX를 표시할 수 있도록 했다.
(7) #define 에 의한 문자열 치환 ================== #define은 문자열의 치환을 하는 프리프로세서 명령이다. 지정을 정의 하는 때 이용한다. 이하의 서식에서 치환전문자열과 치환후 문자열을 지정하는 것으로 빌드시 치환된다.
#define 치환전_문자열 치환후_문자열
이번에는 컴포넌트 버튼을 수치인 상태로는 알기 힘드므로, #define 에서 상수화 하고 있다.
MAC OS X(Server)를 새로 설치하고 XCODE도 새로 설치한 다음, iPHONE을 재 등록하고 device로 출력물이 나오도록 컴파일해 실행하자 다음과 같은 에러 메시지가 나타났다.
[BEROR]Code Sign error: The identity 'iPhone Developer' doesn't match any valid certificate/private key pair in the default keychain
삽질하고 나서 안 거지만 이유는 간단했다. 개발자 등록해서 인증 받은 키가 새로 설치한 MAC OS X에 설치되어 있지 않았고, 또 키가 설치되어있더라도 그 키와 Device Provisioning Profile이 서로 연동되어있지 않았기 때문이다.
그럼 어떻게 해야하는지 설명하겠다. 과거의 인증서 등을 그대로 써도 되는진 모르겠지만, 과거의 기록이 어떤 영향을 줄지 모르므로 무조건 신규발급 받는 것을 택했다. 귀찮으면 걍 패스하고 6번으로 넘어가서 테스트해보라.
0. 유티리티/KeyChain 안의 개발자 인증서와 XCODE/ORGANIZER 안의 Provisioning Profiles를 지운다. (OS를 새로 설치한 상태라면 없을 것이다.)
1. 개발자 홈페이지로 로그인 하고 오른쪽 메뉴의 iOS Provisioning Portal을 선택한다. 2. 왼쪽의 Certificates 메뉴를 클릭. 3. 인증서 맨 오른쪽의 Revoke를 눌러 인증서를 폐기한다. 4. 새로 인증서를 발급받는다. (다른 페이지를 보고 돌아오는 등 조금 기다리면 활성화 된다) 5. Download를 눌러 인증서를 받아 더블클릭하면 키가 추가된다.
사실 이대로만 따라해주면 끝난다. 아주쉽다. 여기서 시키는대로 하면 App ID를 작성하거나 기존 App ID로 Provisioning Profile을 생성해서 넣어주고, 그것을 앞에서 작업한 키와 연동시키는 과정을 수행하도록 짜여있다. 이 과정은 앱 개발자라면 누구나 할 수 있을 정도의 상식선이기 때문에 별도의 설명은 하지 않겠다.
이 과정을 거치지 않으면, 보통 Provisioning Profile이 다음과 같이 나온다. (보통 문제가 되는 상태가 이렇다)
A valid signing identity matching this profile could not be found in your keychain.
이것은 지금 쓰고있는 키와 Provisioning Profile 이 연결 되어있지 않기 때문이다.
KeyCahin의 key를 살펴보면 보통 요런 식으로 나와있을 것이다. (아예 Key에 아무런 값이 없을 경우도 있다. 이 때는 위의 0번 과정으로 가서 새로 등록을 하자. 물론 key명은 사용자마다 각각 다를 것이다.)
하지만, 위에서 나온 과정을 그대로 따라하면 다음과 같이 바뀌어 있음을 알 수 있다.
private key 앞에 삼각형이 달려있다. 이 부분을 클릭해보면...
다음과 같이 연결되어있음을 알 수 있다.
이제 XCODE를 실행하고 다시 컴파일해 보면 device상에서 잘 동작하는 것을 알 수 있다.
사족. 사실 맨 처음 나오는 에러에 추가 설명으로 간단히 개발자 홈에 가서 Launch Assistant를 클릭하라고만 나와주면 "대체 왜 이런 일이 생기는 걸까?" 라고 고민할 필요도 없이 간단할텐데.. 라는 생각을 해 본다.
사족2. 일단 위 내용은 개인 개발자 등록시 발생한 문제에 한한다. 다른 환경에선 어떻게 나오는지 해 본 적이 없어서 위와 똑같다고 장담할 수 없다. 그리고 약간의 기억에 의존하는 부분이 있어서 살짝 설명과 다를 수 있지만 크게 다르진 않을 것이다.
(1)문자열의 폭과 높이의 취득 ========================== 레이블 영역에 대한 문자열의 위치는 가로는 좌,중앙, 우측 정렬을 지정할 수 있으나 세로는 중앙정렬만 가능하고 위 아래는 불가능하다. 따라서 문자열의 묘화와 같이 문자열의 좌상좌표에서 표시 위치를 지정하고 싶은 때는 레이블의 폭과 높이를 문자열의 폭과 높이와 같이 설정한다. 문자열의 푝과 높이를 취득하려면 NSString클래스의 sizeWithFont:메소드를 사용한다.
(중략)
(2)레이블의 생성 ================ 레이블을 생성하려면 UILabel 클래스를 사용한다. alloc으로 메모리를 확보하고 init메소드로 생성한다. 주요 프로퍼티는 다음과 같다. 컴포넌트의 위치와 사이즈는 frame프로퍼티에서 지정한다. frame프로퍼티는 UILabel클래스의 부모 클래스인 UIView클래스에 정의되어 있다.
(중략)
(3)컴포넌트 배치 =============== 컴포넌트를 뷰에 배치하려면 UIView클래스의 addSubview: 메소드를 사용한다. 컴포넌트가 되는 클래스는 UIView클래스를 상송한다. 이번에는 UIVewControl이 가진 뷰에 레이블과 이미지뷰를 추가한다.
(중략)
(4)이미지 뷰 생성 ================ 이미지 뷰를 생성하려면 UIImageView 클래스를 사용한다. alloc으로 메모리를 확보하고 init메소드로 생성한다. 주요 프로퍼티는 다음과 같다.
(중략)
(5) 화면을 단말의방향에 맞춰 회전 =============================== 화면을 단말의 방향에 맞춰 회전시키려면 UIViewController클래스의 shouldAutorotateToInterfaceOrientation:메소드를 오버라이트한다.
4-1-2 MVC 아키택쳐란? ====================== iPhone/iPad 앱은 "모델(Model)", "뷰(View)", "컨트롤러(Controller)"이렇게 3개의 구성요소로 구성되어 "MVC 아키택쳐"라 불린다. 모델은 앱에서 처리하는 데이터, 뷰는 모델의 내용을 화면에 표시하거나 유저로부터 조작을 받거나 하는 것, 컨트롤러는 모델과 뷰를 연결하는 것이다. 앱에서 처리하는 데이터는 모델에 저장된다. 컨트롤러는 그 데이터와 관련된 뷰를 찾아 표시를 경신한다. 유저가 뷰를 조작하면 그 이벤트가 컨트롤러에 통지된다. 컨트롤러는 적절한 처리를 한 뒤 모델의 데이터를 경신한다. 실제 앱을 개발할 때는 UIViewController를 상속한 클래스를 생성해, UIViewController 오브젝트가 가진 view 프로퍼티를 조작하게 된다.
유저의 조작을 통지 데이터 경신 -----------------> ----------------> 뷰 <---------------- 컨트롤러 <--------------- 모델 화면 경신 데이터 경신을 통지 그림 4-1-2 MVC 아키택쳐
4-1-3 애플리케이션 델리게이트 편집 ================================= 이번은 앱 기동시에 불리는 메소드 내에 다음에 설명하는 UIViewController클래스를 상속한 LableEx오브젝트를 생성해, LabelEx 오브젝트의 view 프로퍼티를 윈도우에 추가하는 처리를 기술한다. UIViewController 오브젝트는 인스턴스 변수로 보존된다.
4-1-4 뷰 컨트롤러에 소스 추가 ============================ 이번 프로그램은 2개의 소스코드 "LabelEx.h" "LabelEx.m"과 1개의 레이아웃 파일 "LabelEx.xib"로 구성되어있다. 레이아웃 파일은 Interface builder에서 편집해 이용할 수있는 것이지만, 4-7장에서는 레이아웃 파일을 다음 순서로 작성하지만, 편집은 하지 않는다. 컴포넌트의 배치는 프로그램에서 한다. InterfaceBuilder에 대한 자세한 것은 8장에서 설명한다. iPhone과 iPad에서 같은 소스코드를 사용하므로, Shared 폴더(또는 classes폴더)내에 만든다.
1) Shared폴더를 우클릭해 팝업 "추가->새 파일"을 선택해 Cocoa Touch Class의 UIViewController subclass를 선택하고 "다음"버튼을 누른다. 이때 옵션의 With XIB for user Interface를 체크해 둔다.
그림 4-1-3 뷰 컨트롤러의 소스코드 추가 1)
2) 이름에 "LabelEx.m"을 지정해 "동시에 Label.h도 작성"을 체크하고 "완료"를 누른다.
파라메터를 건냈을 때 취득한 NSTimer 오브젝트의 userInfo 프로퍼티에서 파라메터를 취득할 수 있다.
NSTimer 클래스의 프로퍼티 의미 (id)userInfo rjssps vkfkapxj
NSTimer 오브젝트는 다른 오브젝트형과 달리 scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 메소도에서 생성한 시전ㅁ에 내부에서 1번 retain 되어 타이머 처리종료시에 1회 release된다. 그렇기 때문에 retain/release를 할 필요가 없다.
(2) 타이머의 정지 ================= repeats가 NO일 때는 처리 종료시에 타이머 정지가 되지만, repeats가 YES일 때는 NSTimer클래스의 invalidate메소드에서 정지하거나 애플리케이션 종료할 때까지 루프를 돈다. invalidate메소드를 부른 후는 nil을 대입하는 쪽이 안전하다.
NSTimer 클래스 -(void)invalidate 기능 : 타이머 정지
(3)타이머가 움직이는 상태로는 dealloc은 부를 수 없다 ================================================== NSTimer 오브젝트는 scheduledTimerWithTimeInterval:target:seletor:userInfo:repeats: 메소드에서 생성된 때 인수 target에서 주어진 어브젝트도 retain 한다. 그래서 타이머가 움직이고 있는 중은 target에 주어진 오브젝트의 dealloc 메소드를 부를 수 없다. dealloc 메소드내에서 invalidate메소드를 부르려면 주의하라. 타이머를 움직이고 있는 뷰를 해방할 때 우선 타이머를 invalidate메소드에서 부르고 정지시키고 뷰의 메모리를 해방시키도록 한다. 타이머가 움직이고 있을 때에 dealloc메소드가 불리지 않는 것을 확인하고 싶으면 touchesEnded:withEvent:메소드 끝에 _timer가 nil이 아닐때 UIView클래스의 removeFromSuperview메소드를 불러 보라. 콘솔에 "dealloc 메소드가 불렸다"라고 표시되질 않는다.
(중략)
타이머가 움직이지 않을 때에 dealloc메소드가 불리는 것을 확인하고 싶으면 touchesEnded:withEvent:메소드 끝에 _timer가 nil일 때 UVIew클래스의 removeFromSuperview메소드를 불러 보라. 콘솔에 "dealloc 메소드가 불렸다"라고 표시 된다.
(중략)
(4) 정기 처리 ============ 타이머에 의해 1초간 30회 불리는 onTick:메소드 내에서는 매 프레임 XY좌표에 XY속도를 가산한다. XY좌표가 0미만이거나 화면상이 즈이상이 될 때 XY속도를 반전 시킨다.
UIResponder 클래스의 이하 4개의 터치 메소드를 오버라이드하는 것으로 터치 개시, 터치 무브, 터치 종료, 터치 캔셀 이벤트를 취득할 수 있다. UIResponder 클래스는 UIView클래스가 상속하고있는 클래스로 이벤트 처리를 위해 인터페이스가 정의되어 있다. 다음장에서 설명할 UIViewController클래스도 UIResponder 클래스를 상속하고 있고, UIViewController 클래스에서도 터치 이벤트를 취득할 수 있도록 되어있다.
....(중략)
UITouch 오브젝트군은 터치 위치를 유지하는 UITouch오브젝트의 집합이다. NSSet클래스는 집합을 나타내는 클래스로 allObjects메소드로 NSArray형으로 변환된다.
...(중략)
touchsBegan:withEvent: 메소드로 취득한 UITouch오브젝트를 NSMutableArray형의 _touches변수에 추가하고, touchesEnded:withEvent:메소드로 취득한 UITouch오브젝트를 _touches변수로부터 소거하는 처리를 하고 있다. 이것으로 현재 터치 중 위치를 얻을 수 있다.
(2) 터치 위치의 취득 UITouch 오브젝트로부터 터치 위치를 취득하려면 locationInView:메소드를 사용한다. 터치 위치를 CGPoint치로 취득한다.
(3) 멀티 터치의 유효화 터치 이벤트를 싱글 터치만이 아니라 멀티 터치에 대응시키려면 UIView클래스의 mulitpleTouchEnabled 프로퍼티에 YES를 지정한다.
오브젝트형 인스턴스 변수를 가진 클래스에서는 클래스 해방시에 오브젝트형의 인스턴스 변수를 모두 release합니다. 이번은 ImageEx의 뷰를 애플리케이션 종료할 때까지 해방하지 않으므로, dealloc 메소드는 부르지 않습니다. 애플리케이션 종료시에는 dealloc 메소드는 불르지않고 메모리 리크분도 포함해서 모두 메모리가 해방됩니다. 그러나, 먼 뒤 뷰의 전이를 추가한 때 메모리 해방을 잊지 않기 위해 release는 해두는 것이 좋겠지요. 뷰 해방시에 delegate 메소드가 불리는 것을 확인하는 것은 ImageEx.m에 다음과 같이 두개의 명령을 추가해 주십시오.
//묘화 - (void)drawRect:(CGRect)rect { ...중략..
[self removeFromSuperview]; //<- 추가 }
//메모리 해방 -(void)dealloc { NSLog(@"dealloc메소드가 불렸다"); //<-추가 }
묘화시 최후에 ImageView 오브젝트를 해방하고 메모리 해방시 최초로 콘솔로의 문자열 출력을 합니다. UIView클래스의 removeFromSuperview메소드는 부모 뷰로부터 그 뷰 자신을 소거하는 명령입니다.