iPhone/강좌2009/03/10 21:37
Window는 컨텐츠를 출력하는 역할을 수행하고,  Views는 컨텐츠를 구성하고 그리는 역할을 수행합니다. Window는 MS Windows와 비교하였을 때 메인 다이얼로그, Views는 컨트롤이라고 생각해도 무방합니다. 일반적으로 아이폰 어플리케이션은 1개의 Window와 여러개의  Views로 구성됩니다. 어플리케이션이 생성될 때 UIWindow 클래스의 인스턴스가  아이폰 어플리케이션의 유일한 window가 되고, 그것에 하나 이상의 Views가 포함됩니다. 그 후 UIWindow는 포함된 Views를 출력하는 역할을 수행하게 됩니다. 아이폰 운영체제가 계층적인 windows를 지원하기는 하지만, 1개가 넘는 window를 생성하지 말라고 경고하고 있습니다.

UIWindow는 닫기 버튼이나 타이틀바와 같은 UI를 제공하지 않습니다. 모든 기능은 프로그래밍으로 정의하여 처리해야 합니다. 이것은 모든 아이폰 어플리케이션이 같은 UI 방법으로 시작과 종료를 하기 위한것으로 아이폰의 바탕화면에서 아이콘을 터치하여 프로그램을 시작하고, 홈 버튼을 눌러서 종료하는 것을 위한 것입니다.

어플리케이션을 생성할 때, window의 초기 사이즈는 화면 사이즈에 꽉 차게 생성됩니다. nid나 코드 모든것을 통해서 window는 화면 사이즈보다 작아질 수 없습니다. 화면 사이즈에 맞는 UIWindow는 다음과 같이 UIScreen 을 이용하여 다음과 같은 코드로 생성할 수 있습니다.

<code>
UIWindow * aWindow = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
</code>

아 이폰 어플리케이션에서 view는 대부분 UIView를 상속받아 만들어집니다. 이것은 사용자의 인터페이스를 출력하고, 인터페이스와 통신하는 중요한 두가지 역할을 수행합니다. 모든 view 오브젝트는 지정된 사각 영역 안에서 출력하고 인터페이스의 이벤트를 처리합니다.

하나의 view는 여러개의 sub view를 관리합니다. sub view의 관점에서 그것을 포함하고 있는 view는 superview 또는 parent view라고 불립니다.  sub view 또한 또 다른 sub view를 포함하여 superview가 되는 계층적 구조를 가지고 있습니다.

아이폰 운영체제는 UIKit 이라는 클래스를 제공합니다. UIKit은 UIView를 이용한  window와 각종 컨트롤(views)을 제공합니다. 정말 다양한 views를 제공하기 때문에 다른 운영체제에서 프로그래밍할 때와는 다르게 새로운 컨트롤을 제작할 일이 거의 없습니다. UI 디자인도 그 무엇보다 훌륭하여, 프로그래머가 디자인에 신경쓰지 않고 기본 컨트롤을 그대로 써도 멋진 프로그램이 될 정도입니다. 다른 운영체제(Window)에서는 그렇지 않았죠.

사용자 삽입 이미지

위 그림에서 보는 것과 같이, 정말 많은 컨트롤이 기본적으로 제공되고 있습니다. 각 컨트롤에 대한 자세한 내용은 각각의 컨트롤을 검색하여 사용해보시면 금방 숙지할 수 있습니다.

UIKit은 기본적으로 top-left 좌표계를 사용합니다. 왼쪽 상단이 0,0으로 아래, 오른쪽으로 갈 수록 숫자가 커지는 방식입니다. 다른 운영체제처럼 정수계를 사용하지 않고, floating 형식, 즉 실수계를 사용합니다. 이것은 더 정확히 위치를 잡을 수 있게 하는것에 많은 도움이 됩니다. 예를 들어 300, 300 사이즈를 1/4 로 줄였다가 다시 4배로 정확히 키울 수 있습니다. 다음 그림은 아이폰 스크린과 좌표계의 관계를 나타냅니다.

사용자 삽입 이미지
모든 View 객체는 크기와 위치 속성을 포함함니다. 크기와 위치를 결정하는 속성으로는 Frame과 Bounds라는 두 가지의 중요한 개념이 있습니다. Frame은 출력되는 화면 영역을 의미하고, Bounds는 출력할 데이트에서의 영역입니다. 아래 그림을 보면 이 개념을 확실하게 이해할 수 있습니다. 왼쪽에 있는 그림 데이터에서 사용할 영역을 지정한것이 Bounds 개념이고, 이 Bounds를 화면 영역의 어느 부분에 출력할 지 결정하는 좌표가 Frame 영역입니다.

사용자 삽입 이미지
또 한가지 속성에 Center 값이 있습니다. 이 값은 Frame 영역에서의 가운데 좌표(X, Y)를 의미합니다. Bounds 영역에서의 Center가 아니라는 점을 꼭 명심하고 있어야, 나중에 개발 할 때 헷갈림이 없을 것입니다.

View 객체가 생성될 때 위 세가지 (Bounds, Frame, Center) 속성이 다음과 같이 초기화 됩니다.
Bounds rectangle 의 원점(Top-Left)를 0, 0으로 설정하고, 가로, 세로를(Width, Height)를 Frame(화면 영역)과 같게 설정합니다. 그리고 Center 속성을 Frame의 width/2, height/2 즉 가운데로 설정합니다. 이 세가지의 속성은 각각을 수정해도 다른 두가지의 속성에 다음과 같이 영양을 끼칩니다.

* Frame 속성을 설정하면 Bounds가 Frame의 사이즈에 맞게 확장됩니다. 그리고 Center 속성이 새로운 Frame에 맞게 변화합니다.
* Center 속성을 설정하면 Frame 속성의 원점이 변경됩니다. 즉, Frame의 크기가 변경되는 것이 아니고 Frame의 가운데를 손가락으로 잡고 움직여서 Frame 자체를 움직이는 듯한 효과가 있습니다.
* Bounds의 사이즈를 변경하면 Frame이 그 크기에 맞춰서 변화합니다.
* Bounds의 원점은 두개의 속성(Frame, Center)의 변경 없이도 바꿀 수 있습니다. 아래 그림과 같이 Bounds의 원점을 대각선 아래로 변경하여 그림의 다른 부분을 똑같은 Frame 에 출력되도록 할 수 있습니다.

사용자 삽입 이미지
이로서 아이폰의 윈도우, 뷰, 좌표계의 개요를 마무리 하겠습니다. 다음 강좌에서는 각 좌표를 변환(회전, 이동, 확대, 축소)등을 수행할 수 있는 방법을 소개하고 실질적으로 실습하는 모습을 보여드리겠습니다.

Posted by Getroot

Leave your greetings.

  1. find cheap <a href="http://www.echeapraybansunglasses.com/"><strong>ray ban aviator</strong></a> products on the <a href="http://www.echeapraybansunglasses.com/"><strong>ray ban sunglasses</strong></a> store, then you can see the fashion <a href="http://www.echeapraybansunglasses.com/"><strong>aviator sunglasses</strong></a>, enjor to buy <a href="http://www.echeapraybansunglasses.com/"><strong>raybands</strong></a> gooo.

    2011/10/20 11:43 [ Permalink : Modify/Delete : Reply ]

iPhone/강좌2009/03/05 19:08
iPhone OS에서 실행되는 어플리케이션은 Core OS와 Core Services 프레임워크를 분류하여 로컬 파일시스템과 네트워크에 접근할 수 있습니다.

또한 사용자의 데이터를 저장하거나 다음 사용을 위한 어플리케이션의 상태를 저장하기 위해 파일을 읽고 쓰는 것이 가능합니다.

네트워크에 접근함으로써 네트워크상의 서버와 통신하거나 데이터를 주고 받는 등의 원격 작업을 수행할 수 있는 기능을 갖출 수 있습니다.

iPhone OS의 파일은 사용자들의 미디어파일과 개인정보와 같은 파일들과 함께 Flash 메모리를 공유하여 사용하고 있습니다.

보안상의 이유로 개발자가 만든 어플리케이션은 자신이 위치한 자신의 디렉토리에 국한하여 제한적인 데이터 읽기, 쓰기만을 할 수 있습니다. 

Commonly Used Directories

보안을 위해 어플리케이션은 몇몇에 불과한 디렉토리에 접근하여 데이터와 환경 설정을 읽고 쓸수 있습니다.

어플리케이션이 장치에 설치될때 어플리케이션을 위한 홈 디렉토리가 생성됩니다.

다음은 그 내부의 중요한 서브디렉토리의 설명입니다.

<Application_Home>/AppName.app

이 디렉토리는 어플리케이션 자신을 포함하는 Bundle 디렉토리입니다.
실행중에 이 디렉토리 내부의 파일을 변경할 수 없습니다.
iPhone OS 2.1 혹은 그 이후의 버젼은 iTunes에서 이 디렉토리를 백업하지 않습니다.
그러나 어플리케이션을 구매했을 당시의 초기 싱크 과정에서는 컨텐츠 내용이 복사됩니다.

<Application_Home>/Documents/

이 디렉토리는 어플리케이션이 특정 데이터를 저장하기 위해 사용되는 공간입니다.
사용자의 데이터나 다른 정보를 정기적으로 저장할 수 있습니다.
이 디렉토리 내부의 컨텐츠는 iTunes에 의해서 백업 됩니다.

<Application_Home>/Library/Preferences

이 디렉토리는 어플리케이션의 특정 설정 파일을 포함합니다.
하지만 환경 설정 파일을 직접 생성하여서는 안되고 NSUserDefaults 클래스나 CFPreferences API를 사용해야 합니다.
이 디렉토리 내부의 컨텐츠는 iTunes에 의해서 백업 됩니다.

<Application_Home>/Library/Caches

이 디렉토리는 어플리케이션의 실행때마다 지속적으로 사용해야 하는 파일을 읽고 쓰기 위해 사용합니다.
개발한 어플리케이션은 일반적으로 이곳에 파일을 추가하고 삭제하여야 합니다.
iTunes는 전체 복원시에 이 디렉토리의 내용을 모두 제거 합니다.
그러므로 어플리케이션은 필요할때마다 이곳의 파일을 생성해 낼 수 있어야 합니다.
iPhone OS 2.2 혹은 그 이후의 버젼은 iTunes에서 이 디렉토리를 백업하지 않습니다.

<Application_Home>/tmp/

이 디렉토리는 어플리케이션이 실행때마다 지속될 필요가 없는 임시적인 파일을 읽고 쓰기 위해 사용합니다.
어플리케이션이 실행중이 아닐때 시스템은 파일이 더 필요한가를 판단하여 더이상 필요하지 않다고 판단이 되면 삭제합니다.
iPhone OS 2.1 혹은 그 이후의 버젼에서는 iTunes에서 이 디렉토리를 백업하지 않습니다.

Backup and Restore

어플리케이션이 백업과 복원을 위해 따로 준비해야 하는 것은 없습니다.

iPhone OS 2.2 혹은 그 이후의 버젼은 장치가 컴퓨터에 연결되어 싱크 될때 다음을 제외한 나머지 모든 파일을 증분 백업 합니다.
<Application_Home>/AppName.app
<Application_Home>/Library/Caches
<Application_Home>/tmp

어플리케이션이 매우 큰 파일을 생성하거나 매우 빈번하게 파일 엑세스를 한다면 /Documents가 아닌 /Library/Caches를 이용하는 것이 좋습니다. /Documents에 저장하게 되면 백업/복원 작업시에 시간이 더 걸리게 되는 문제점이 될 수도 있습니다.

Getting Path to Application Directories

다양한 레벨의 시스템에서 어플리케이션의 샌드박스의 위치를 알아내기 위한 다양한 프로그래밍 방식이 있어왔습니다.

하지만 Cocoa는 이러한 경로를 탐색하기 위해 다양한 프로그래밍 인터페이스를 제공합니다.

NSHomeDirectory(Foundation 프레임워크에 포함) 함수는 홈디렉토리의 경로뿐만 아니라 Documents, Library 그리고 tmp같은 디렉토리 경로를 손쉽게 얻어올수 있습니다.

또한 추가적으로 NSSearchPathForDirectoriesInDomains와 NSTemporaryDirectory함수를 사용하여 정확한 Document, Caches, tmp 디렉토리 경로를 얻어올 수 있습니다.

이 NSSearchPathForDirectoriesInDomains 함수를 사용하여 어플리케이션과 연관된 전체 경로를 알 수 있습니다.

iPhone OS에서 이 기능을 사용할려면 적절한 검색 경로를 첫번째 매개 변수로, NSUserDomainMask를 두번째 매개 변수로 지정합니다.

일반적으로 자주 사용되는 경로 상수는 다음과 같은 것들이 있습니다.

NSDocumentDirectory
<Application_Home>/Documents

NSCachesDirectory
<Application_Home>/Library/Caches

NSApplicationSupportDirectory
<Application_Home>/Library/Application Support


어플리케이션의 Documents/ 디렉토리를 찾기 위해 다음과 같이 사용합니다.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

위의 결과값을 찍어보면 /var/mobile/Applications/해쉬코드/Documents 와 같은 값이 나옵니다. 해당 경로가 어플리케이션 Documents 디렉토리의 절대 경로가 되겠죠.

두번째 인자인 NSUserDomainMask말고 다른것을 사용할 수 있습니다.

NSSystemDomainMask와 첫번째 인자로 NSApplicationDirectory로 검색하여 보면 /Applications 라는 시스템 경로를 얻을 수 있습니다.

Reading and Writing File Data

iPhone OS는 파일을 읽고 쓰고 관리할 수 있도록 몇가지 방법을 제공하고 있습니다.

Foundation Framework :
  • 어플리케이션이 Property List를 사용한다면 NSPropertyListSerialization API를 사용하여 NSData로 변환이 가능합니다.  이렇게 변환 후에 NSData의 메서드를 이용하여 데이터를 쓸 수 있습니다.
  • 어플리케이션의 Model객체가 NSCoding 프로토콜을 채택하였다면 NSKeyedArchiver 클래스를 이용하여 객체를 저장할 수 있습니다.
  • Foundation 프레임워크는 컨텐츠 파일의 랜덤 엑세스를 위한 NSFileHandle 클래스를 제공합니다.
  • Foundation 프레임워크안의 NSFileManager는 시스템에 있는 파일을 생성하고 조작하는 메서드를 제공합니다.

Core OS calls :

  • fopen, fread, fwrite 를 호출하여 데이터를 엑덤 엑세스를 통해 읽고 쓸 수 있습니다.
  • mmap과 munmap을 호출하여 효과적으로 큰 파일을 메모리로 로드하거나 그안의 컨텐츠에 접근할 수 있습니다.

Reading and Writing Property List Data

Property List는 캡슐화 되어있는 데이터를 말합니다. 배열, 문자열, 날짜, 이진데이터, 숫자 및 Boolean 값을 포함합니다.

다른 예로 모든 Cocoa/iPhone 어플리케이션의 설정이 들어있는 Info.plist 파일을 들 수 있습니다.

코드상에서는 NSDictionary, NSArray, NSString, NSDate, NSData, NSNumber를 사용할 수 있습니다.

다음은 Property List를 NSData로 변환하여 저장하는 방법입니다.

- (BOOL)writeApplicationPlist:(id)plist toFile:(NSString *)fileName {
NSString *error;
NSData *pData = [NSPropertyListSerialization dataFromPropertyList:plist
format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
if (!pData) {
NSLog(@"%@", error);
return NO;
}
return ([self writeApplicationData:pData toFile:(NSString *)fileName]);
}
- (BOOL)writeApplicationData:(NSData *)data toFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
if (!documentsDirectory) {
NSLog(@"Documents directory not found!");
return NO;
}
NSString *appFile = [documentsDirectorystringByAppendingPathComponent:fileName];
return ([data writeToFile:appFile atomically:YES]);
}

그러면 읽어와야 겠죠. 다음의 코드를 참고하세요.
- (id)applicationPlistFromFile:(NSString *)fileName {
NSData *retData;
NSString *error;
id retPlist;
NSPropertyListFormat format;
retData = [self applicationDataFromFile:fileName];
if (!retData) {
NSLog(@"Data file not returned.");
return nil;
}
retPlist = [NSPropertyListSerialization propertyListFromData:retData
mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if (!retPlist){
NSLog(@"Plist not returned, error: %@", error);
}
return retPlist;
}
- (NSData *)applicationDataFromFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
NSData *myData = [[[NSData alloc] initWithContentsOfFile:appFile] autorelease];
return myData;
}

id는 객체 자신을 가리키는 타입입니다. 무엇이든 가리킬 수 있는 자바의 Object라고 생각하시면 이해가 쉬울까요.

Using Archivers to Read and Write Data

실제로 프로그램이 실행할때 메모리에 수많은 객체를 생성합니다. 그리고 각각의 객체는 서로 연결되어있습니다.

이러한 객체를 파일로 저장후에 다시 읽었을때 연결된 다른 객체가 존재하지 않는다면 큰 문제가 되겠죠.

아카이브라 불리는 이 과정은 실행중에 생성된 객체들의 복잡한 상속관계를 나타내는 그래프를 모두 바이트 단위로 변환하는 과정입니다.

이것을 사용하기 위해서는 클래스가 NSCoding을 구현해야 하고 그에 따른 encodeWithCode와 initWithCoder 메서드를 구현해야 합니다.

자세한 내용은 관련 [문서]를 참고하세요. 데이터를 저장하기 위해서는 다음과 같은 방식으로 합니다.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[_myDataSource representation]];
[data writeToFile:myFilePath atomically:YES];

데이터를 읽어 들여 복원할때는 다음과 같이 하면 됩니다.
NSData* data = [NSData dataWithContentsOfFile:myFilePath];
id rootObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

File Acess Guidelines

파일을 생성하고 데이터를 쓸때 다음의 가이드 라인을 지키도록 합시다.

파일을 쓰는 횟수를 최소화 해야 합니다. 파일을 엑세스 하는 작업은 느리고 제한적인 수명을 가진 플래시 메모리에 하는 행위입니다.

  • 파일의 일부분이 변경되었을때 바뀐 부분만을 쓸 수 있습니다. 적은 바이트의 변경을 위해 전체 파일을 쓰는 행위를 피하도록 합시다.
  • 파일 포맷을 정의할때, 자주쓰이는 데이터를 최대한 하나의 그룹으로 묶어 저장하면 디스크 엑세스 횟수를 최소화 할 수 있습니다.
  • 랜덤한 엑세스가 필요한 데이터 구조로 되어있는 경우 SQLite 데이터베이스에 데이터를 저장할 수 있습니다.

캐쉬 파일을 디스크에 쓰는것을 피하도록 합니다. 어플리케이션이 종료될때 다음 실행시에 동일한 상태로 시작하기 위해 저장하는 것은 예외로 합니다.

Saving State Information

사용자가 Home 버튼을 누르게 되면 iPhone OS는 당신의 어플리케이션을 종료하고 홈스크린 화면으로 돌아갑니다.

마찬가지로 URL을 여는 경우 어플리케이션이 종료하고 다른 어플리케이션의 URI가 열리게 됩니다.

이 과정은 멀티테스트 환경과 달리 현재 실행되는 어플리케이션이 종료되고 다른 어플리케이션이 수행되는 것을 뜻합니다.

이러한 작업이 자주 일어난다면 어플리케이션의 상태를 관리하는 방법을 변경할 필요가 있습니다.

사용자가 수동으로 디스크에 저장하는 작업과 달리 주요 지점에서 자동으로 저장하도록 프로그램이 변경되어야 합니다.

어플리케이션이 종료될 때 임시 캐시 파일이나 데이터베이스 환경을 이용하여 상태를 저장해야 합니다.

다음에 어플리케이션이 실행될때 저장되었던 정보를 가지고 이전 상태로 복원하는 과정을 수행하여야 합니다.

저장하는 횟수를 최소화 하여야 하지만 최대한 적절한 시점에 저장하여 이 상태를 그대로 복원하여 보여주어야 합니다.

예를 들어 연락처를 수정하고 있다가 어플리케이션을 종료하고 다시 실행했을 때 연락처의 가장 첫화면보다 수정하던 창을 그대로 보여주는 것이 좋습니다.

Networking

iPhone OS의 네트워킹 스택은 아이폰과 아이팟 터치에 있는 무선 하드웨어 장치의 여러 인터페이스를 포함합니다.

주요 프로그래밍 인터페이스로는 BSD소켓으로 만들어진 CFNetwork 프레임워크가 있습니다.

이 문서에서는 간략한 조언만을 담고 있고 NSStream 클래스를 사용하는 방법의 정보를 원하시면 Foundation Framework 문서를 참고하시기 바랍니다.

Tip for Efficient Networking
네트워크를 통해 데이터를 주고받는 행위는 장치의 많은 전력을 소비하는 행위라는것을 기억하셔야 합니다.
데이터를 주고받는 시간을 최소화 하는 것이 배터리 수명에 도움이 됩니다.

  • 통신 프로토콜을 정의할때 데이터 포맷은 가능한한 작게 만들어야 합니다.
  • 채팅과 같은 무수히 많은 통신이 오가는 방식은 피하도록 합니다.
  • 데이터는 하나의 덩어리로 묶어 보낼 수 있습니다.

Cellular와 Wi-Fi를 이용한 통신은 아무런 행동이 없을때 파워가 꺼지도록 만들어져 있습니다. 하지만 계속해서 작은 용량의 패킷을 주고 받게 되면 이것은 지속적으로 파워가 켜져있게 되는 것을 의미하게 되며 배터리가 소모된다는 것을 뜻합니다.

그러므로 작은 데이터를 자주 주고 받는것보다는 큰 데이터를 긴 시간차를 두고 주고 받는 것이 좋습니다.

네트워크를 이용한 통신 프로그램을 작성할때는 패킷의 손실이 일어날 수 있다는 것을 기억해 두어야 합니다.

코드를 작성할때 통신 실패에 대한 핸들링을 충분히 하여야 합니다.

그 예로 네트워크가 갑자기 사라지게 될 경우 사용자에게 알리는 시스템을 갖추는 등의 노력이 필요합니다.

Posted by 아이

Leave your greetings.

iPhone/강좌2009/02/13 16:23
Event Handling

iPhone의 운영체제는 기본적으로 멀티 터치를 지원합니다. iPhone은 기존에 사용자가 이용하던 마우스나 키보드를 벗어나, 화면에 보이는 객체를 자유 자재로 선택하여 조작할 수 있는 환경을 제공합니다. 이러한 터치 이벤트는 다음과 같이 크게 3가지로 분류되어 view에서 메시지를 수신하여 처리할 수 있습니다.

* touchesBegan
prototype:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
설명:
사용자가 화면에 손가락을 대면 호출됩니다.

* touchesEnded
prototype:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
설명:
사용자가 화면에서 손가락을 떼면 호출됩니다.

* touchesMoved
prototype:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
설명:
사용자가 화면에 손가락을 대고 드래그 동작을 하면 호출됩니다.

iPhone에서위와 같은 이벤트가 호출되는 순서를 그림으로 나타내면 다음과 같습니다.

사용자 삽입 이미지

사용자가 손가락 1개를 화면에 터치 하였을 때 UITouchPhaseBegan, 즉 touchesBegan 메시지를 수신하고 또 다른 손가락이 터치 되었을 때 다시 한번 touchesBegan 메시지가 수신됩니다. 이 상태에서 손가락을 떼지 않고 확대/축소를 하기 위해 움직이면 UITouchPhaseMoved, 즉 touchesMoved 메시지가 수신되고 마지막으로 손가락을 떼면 UITouchPhaseEnded, 즉 touchesEnded 메시지가 수신됩니다.

각각의 이벤트는 1-touch와 n-touch 모두 동일하게 발생 되며, UIControl을 상속받아 구현된 UIButton이나 UISlider등의 컨트롤은 이미 이러한 터치 이벤트를 처리하고 있기 때문에 개발자가 기본적인 기능만을 이용한다면 위의 이벤트를 처리할 필요가 없습니다. 하지만, Safari나 Google map같이 페이지를 확대/축소 하거나 위/아래로 이동하기 위해서는 해당 view에서 터치 이벤트를 수신하여 처리하여야 합니다.

HAN
위와 같은 이벤트를 처리하기 위해서는 UIView 클래스를 상속받은 View 클래스를 생성하고 그 클래스 안에서 메시지를 받아서 처리하여야 합니다. 이러한 동작을 하는 프로젝트를 생성하는 방법은 다음과 같습니다.

1. "Xcode"를 실행한 뒤 새 프로젝트를 생성합니다.

2. 다음 그림과 같이 "New Project" 창에서 "Windows-Based Application"을 선택한 뒤 "Choose" 버튼을 누릅니다.

사용자 삽입 이미지


3. 이번엔 생성하고자 하는 프로젝트 이름을 입력합니다. 여기서는 "EventHandling" 이라는 이름으로 프로젝트를 생성하겠습니다.

사용자 삽입 이미지


4. 아래 그림은 "Windows-Based Application"으로 생성된 가장 기본적인 응용 프로그램의 모습입니다. 이 중 "MainWindow.xib" 파일은 View나 기타 화면에 관련된 리소스를 포함하고 있는 파일이며, 새로운 View 클래스를 생성하기 위해 이 파일을 더블클릭 하여 "Interface Builder"를 실행합니다.

사용자 삽입 이미지


5. 다음은 "Interface Builder"의 모습입니다. 새로운 View를 추가하기 위해 "Library" 도구 창의 "View"를 드래그 하여 "Window"창으로 드랍 합니다.

사용자 삽입 이미지


6. View 의 위치가 "Window"안에 꽉 차도록 적당히 레이아웃을 배치합니다.

사용자 삽입 이미지


7. 이번에는 새로 생성된 View에 label을 추가합니다. View와 마찬가지로 "Library" 도구 창의 "Label"을 드래그 하여 "Window" 창으로 드랍 합니다.

사용자 삽입 이미지


8. "Label"을 "Window" 창으로 드랍한 뒤의 모습입니다.

사용자 삽입 이미지


9. "Label"의 위치와 "Text"값을 통해 내용을 수정합니다. 또한 Baseline, Layout 등을 통해 정렬 상태를 변경할 수 있습니다.

사용자 삽입 이미지


10. 이번에는 실제 코드에 사용될 UIView 클래스를 상속받은 View를 생성하기 위해 View의 클래스 이름을 변경합니다. "Window"창의 View를 선택한 뒤 "MyUI View Identity" 도구 창(4번째 탭)의 "Class"에 생성하고자 하는 클래스 이름을 입력합니다. 여기서는 "MyUIView"를 입력하였습니다.

사용자 삽입 이미지


11. 이번에는 "Label"을 코드와 연결하기 위한 선언을 합니다. "MyUI View Identity" 도구 창(4번째 탭)에서 "Class Outlets"의 하단에 있는 "+" 버튼을 눌러 새로운 항목을 생성하고, 해당 항목의 "Outlet"을 지정합니다. 여기서는 "helloLabel"를 입력하였습니다. 또한 이 항목의 데이터 형을 "UILabel"로 지정하기 위해 오른쪽 "Type"에 "UILabel"이라고 입력합니다.

사용자 삽입 이미지


12. 여기까지 작업이 완료되었다면, label과 실제 UI 상의 객체와 매핑 하는 일이 남았습니다. "MyUI View Connections" 도구 창(2번째 탭)에서 "Outlets"에 있는 "helloLabel"의 오른쪽 원 가운데를 드래그 하여 "Window"창의 "Label" 객체에 드래그 합니다. 아래 그림과 같이 드래그 중에 매핑 될 객체의 정보가 간단히 툴팁으로 표시됩니다.

사용자 삽입 이미지


13. 모든 작업이 완료된 후의 모습입니다.

사용자 삽입 이미지


14. 지금까지 작업한 내용을 저장합니다.

사용자 삽입 이미지


15. 저장한 결과를 바탕으로 실제 클래스 파일을 생성하기 위해 "Window"창의 "View"를 선택한 후 메뉴의 "File"-"Write Class Files..."를 선택합니다.

사용자 삽입 이미지


16. 기본적으로 위에서 입력한 "MyUIView"가 표시됩니다. 만약 윗 단계에서 "View"를 선택하지 않으면 다른 클래스를 저장하는 창이 나옵니다.

사용자 삽입 이미지


17. 생성한 클래스를 현재 프로젝트에 추가할 것인지 선택합니다. 사용할 예정이기 때문에 체크한 뒤 "Add"를 눌러 추가합니다.

사용자 삽입 이미지


18. 모든 UI 작업이 끝났으므로 "Interface Builder"를 종료합니다.

사용자 삽입 이미지


19. 최종적으로 생성된 UI 코드 입니다.

사용자 삽입 이미지


위의 방법으로 생성된 코드를 이용하여 터치 이벤트를 핸들링 하는 예제를 작성하여 보겠습니다.

자동으로 생성된 클래스의 부모 클래스를 지정하기 위해 "MyUIView.h"에서 다음과 같이 주석처리 되어 있는 부분을 수정합니다. 또한, 이벤트 처리를 위한 함수를 추가합니다.

@interface MyUIView : /* Specify a superclass (eg: NSObject or NSView) */ {
IBOutlet UILabel *helloLabel;
}

@end


@interface MyUIView : UIView {
IBOutlet UILabel *helloLabel;
}

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

@end


또한, 이벤트 핸들링을 수행하는 함수를 "MyUIView.m" 파일에 추가하고 각각의 함수를 채웁니다.

#import "MyUIView.h"

@implementation MyUIView

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
helloLabel.text = @"Began";
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
helloLabel.text = @"Ended";
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
helloLabel.text = @"Moved";
}

@end


위 코드에서는 각각의 이벤트가 발생하면 "helloLabel"의 Text를 바꿈으로써 알 수 있도록 하였습니다.
이제 코드를 컴파일 한 뒤 실행하면 터치/드래그 할 때마다 Label의 텍스트가 변경하는 것을 볼 수 있습니다.

여기에서 좀 더 확장하여 터치 후 드래그시 Label이 드래깅 되는 부분을 따라다니도록 수정 해보겠습니다. "touchesMoved" 함수를 수정하여 원하는 기능을 수행할 수 있습니다.

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
helloLabel.text = @"Moved";

UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self];

CGRect labelPosition = [helloLabel frame];

labelPosition.origin.x = currentTouchPosition.x;
labelPosition.origin.y = currentTouchPosition.y;

[helloLabel setFrame:labelPosition];
}


위와 같이 수정한 뒤 iPhone 에뮬레이터를 실행하면 드래그 할 때마다 Label의 위치가 변경되는 것을 볼 수 있습니다.



사용자 삽입 이미지

아래는 본 실습에 사용된 코드 입니다.


Posted by AccessDenied

Leave your greetings.

iPhone/강좌2009/02/13 14:03
아이폰 어플리케이션 개발이 많은 이슈가 되고 있고, 개발자들의 관심이 날로 증가하고 있습니다. 저희 아이렌소프트도 현재 아이폰 어플리케이션 개발에 착수하였습니다. 이에 아이폰 어플리케이션에 대한 공부를 시작하고 있는데, 이를 내부적으로만 사용하기 보다는 여러 개발자분들과 함께 토론하며 같이 성장하자는 의미에서 앞으로 공부하며 나오는 산출물들을 공개하기로 하였습니다. 첫번째로 애들에서 제공하는 문서인 iPhone Application Programming Guide 를 이용하여 공부하는 내용을 챕터벌로 올릴 예정입니다. 그리고 중간중간 저희가 먼저 삽질했던 부분을 정리하여 팁 & 태크 형식으로 올릴 것 입니다. 또한 기획인 관점에서 인기있는 아이폰 어플리케이션의 리뷰를 진행할 것이며, 아이폰에 대한 새로운 소식을 접하게 되면 그것들을 소개드릴 것 입니다. 아무쪼록 많은 개발자와 기획자 분들도 동참하시어 서로 정보를 공유하며 함께 많은 발전 이루기를 진심으로 기원해봅니다. 아이폰의 한국 출시가 거의 확정되어 있는 상황에서 아이폰 커뮤니티로 발전해 나가길 바랍니다. 먼저 아이폰의 Core에 대해 소개해 드리겠습니다.
 
The Core Application


모든 아이폰 어플리케이션은 UIKit 프레임워크를 사용하여 만들어집니다. 그러므로 모든 아이폰 어플리케이션의 코어(Core)는 같은 구조를 가지고 있습니다. 이것은 대부분의 운영체제가 제공하는 방식이므로 크게 특별한 부분은 없습니다. 윈도우는 MFC나 Windows API를 제공한다는 것과 리눅스 또한 Linux API를 이용하여 프로그래밍 한다는 것과 같습니다.

Core Application Architecture

어플리케이션이 동작한 순간부터 끝날때까지 UIKit 프레임워크는 어플리케이션의 주요 구조의 대부분을 관리합니다. 아이폰 어플리케이션은 시스템으로 부터 계속 이벤트를 받아서 응답합니다. 이벤트를 받는것은 UIApplication 오브젝트의 역할입니다. 하지만 이벤트에 대한 응답은 사용자가 작성하는 코드에서 이루어집니다. 어디서 이벤트에 대한 응답을 할 것인지를 이해하기 위해서는 life cycle과 event cycle에 대한 이해가 필요합니다. 다음 챕터에서는 이 cycles와 아이폰 프로그램의 주요 디자인 패턴에 대해 설명합니다.



The Application Life Cycle

어플리케이션 life cycle은 시작과 종료 사이에 발생시키는 이벤트의 부분으로 구성됩니다. 어플리케이션이 시작 될 때 아이폰 어플리케이션도 main 함수로부터 시작되는데 이때 UIKit은 어클리케이션의 UI를 로드하고 이벤트를 받을 준비를 합니다. 프로그램이 동작하는 동안 UIKit은 이벤트를 사용자의 오브젝트에 전달하고 명령에 응답하는 역할을 수행합니다. 사용자가 프로그램을 종료시킬 때, UIKit은 어플리케이션에 종료 메시지를 전달하고, 프로세스의 종료를 시작하는 역할까지 수행하게 됩니다. 그림 1은 이러한 어플리케이션 라이프사이클을 도표화 한 것 입니다. 그림 1에서 보면 UIKit에서는 처음에 사용자의 탭으로 어플리케이션이 수행됩니다. 그때 appliationDidFinishLaunching를 통해 어플리케이션을 제어할 수 있습니다. 그 후 main()함수가 실행되고 UIApplicationMain 함수를 통해 이벤트를 받고 어플리케이션으로 전달하며, 어플리케이션은 Handle event를 통해 어플리케이션을 제어합니다. 어플리케이션이 정상적으로 동작하다가 사용자나 시스템이 어플리케이션 종료를 시도하면 applicationWillTerminate 메시지가 발생합니다. 사용자는 이 메시지를 받아서 종료시 처리해야 할 것을 수행 할 수 있습니다. 그 후 어플리케이션이 종료되는 것이 모든 아이폰 어플리케이션의 life cycle입니다.

사용자 삽입 이미지

그림 1. 어플리케이션 라이프 사이클


The Main Function

아아폰 어플리케이션에서 main 함수는 오직 굉장히 작은 일만을 수행합니다. 물론 아주 특별한 예외때 main 함수를 수정하여 사용할 수 있지만, 대부분의 어플리케이션의 main 함수는 코드 1과 같습니다.

#import <UIKit/UIKit.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}

main 함수는 오직 세가지의 역할만을 수행합니다. 첫번째로 autorealese pool을 생성하고, UIApplicationMain함수를 호출합니다. 마지막으로 autorelease pool을 릴리즈하고 프로그램을 종료하게 됩니다.

Autorelease는 별도의 글을 통해 자세히 소개하기로 하고, 우선 UIApplicationMain 함수를 살펴보겠습니다. UIApplicationMain 함수는 네개의 인자(Parameters)를 받습니다.첫번째 두번째 인자인 argc, argv는 main 함수로 받는 것으로 쉘에서 프로그램을 실행 할 때 프로그램 실행 명령어와 함께 인자로 들어오는 값을 넣습니다. 그리고 세번째로 주요 클래스의 ID와, 네번째로 어플리케이션의 delegate 클래스를 넣습니다. 여기서 delegate 란 다른 클래스에 어플리케이션의 동작을 위임하기 위해 작성된 디자인 패턴으로, 프로토콜 규칙에 따라 구조를 맞추고, 필요한 메소드를 구현하면 그 클래스가 알아서 동작하게 되는 디자인 패턴을 말합니다. UIApplicationMain의 세번째 네번째 인자에  nil 을 넣으면 UIKit 은 UIApplication 클래스를 기본으로 사용하여, 네번째 인자에 nil을 넣으면 UIKit은 어플리케이션 delegate 가 nib 파일을 통해 로딩 될 것이라고 가정합니다.

The Application Delegate

사용자 레벨(High level)에서의 행동을 모니터링 하는 것은 개발자가 제공하는 delegate 오브젝트의 역할입니다. Delegation(위임) 방식은 UIKit 을 subclassing 하는 복잡함을 방지하는 매우 좋은 디자인 패턴입니다.


The Main Nib File

어플리케이션이 로딩될 때 수행하는 또다른 일 중 하나는 어플리케이션의 nib 파일을 로딩하는 것 입니다. 만약 어플리케이션의 Info.plist 파일이 NSMainNibFile key를 가지고 있다면 UIApplication은 nib 파일이 정의하는 것을 로드 합니다. main nib 파일은 자동으로 로드되는 한 하나의 nib 파일 입니다. 그러나 필요할 때 nib 파일을 사용자가 집접 로드할 수는 있습니다. Nib 파일은 디스크에 저장되는 하나 이상의 오브젝트의 스냅샷이 저장되어 있는 리소스(resource) 파일입니다. Main nib 파일은 일반적으로 window 오브젝트와 delegate 오브젝트가 포함되어 있습니다. 그리고 하나 이상의 윈도우를 제어 하기 위한 key 오브젝트로 구성되어 있습니다. nib 파일을 로딩하면 실제 메모리에 이 오브젝트를 재 구성하여 사용 가능한 상태로 만듭니다. 이것은 UIApplicatonMain에서 로딩하는 것과 동작 상 다른 점이 없습니다.


The Event-Handling Cycle

UIApplicationMain 함수가 초기화 된 후에, 어플리케이션의 이벤트 관리와 그림 그리기를 시작합니다. 이것은 그림 2에 묘사되어 있습니다.

사용자 삽입 이미지

그림 2. 이벤트와 그리기 주기

그림 2는 보면, 운영체제(Operating system)은 사용자의 터치를 감지하여 Event queue에 넣고, Event queu는 어플리케이션에 이벤트를 전달합니다. 어플리케이션 내부에서는 이벤트를 사용해야 하는 오브젝트에 이를 전달합니다. 동작이 완료되면 다시 운영체제로 결과를 돌려주고, 운영체제는 이를 화면에 표현합니다.
아이폰 어플리케이션에서는 멀티 터치 이벤트를 지원합니다. 하지만 멀티 터치가 여러개의 이벤트를 생성하는 것이 아닌, UIEvent라는 하나의 이벤트에 캡슐화 되어 들어가 있습니다. 터치에 관련된 이벤트는 UIEvent에 UITouch 오브젝트를  포함하여 전달하게 됩니다.

어플리케이션이 실행되면, 시스템은 어플리케이션을 위해 프로세스와 하나의 쓰레드를 생성합니다. 생성된 쓰레드는 어플리케이션의 메인 쓰레드가 되고, 이것은 UIApplicaton이 mian run loop 에서 동작합니다. 그림 3은 main run loop 와 이벤트 제어의 관계를 나타냅니다.

사용자 삽입 이미지

그림 3. Main run loop

UIApplication 오브젝트는 입력되는 터치 이벤트를 적당한 responder 오브젝트로 분기하여 mian run loop를 설정합니다. responder 오브젝트는 UIResponder 를 상속받아 만들어지며, UIApplication, UIWindow, UIView, 그리고 모든 UIView의 서브 클래스의 인스턴스를 포함합니다.

추가로, UIResponder 클래스는 responder chain 을 가지고 있습니다. 이것은 만들어지는 이벤트가 링크로 연결되어 전달되며, 첫번째 responder가 이벤트를 처리하지 못하면, 두번째 responder 에 전달되고, 이렇게 끝까지 전달되면서 처리하는 구조입니다.

Fundamental Design Patterns

디자인 패턴에 대한 부분은 별도의 팁 & 태크에서 소개해 드리도록 하겠습니다.


The Application Runtime Environment

Fase Launch, Short Use

아이폰은 동시에 하나의 어플리케이션만 실행할 수 있습니다. 이것은 어플리케이션을 실행하기 위해서는 항상 홈 스크린에서 어플리케이션 아이콘을 클릭하여 실행해야 한다는 것을 의미합니다. 아이폰 어플리케이션은 빠르게 실행되고 빠르게 종료되어야 합니다. 만약 5초 이내로 어플리케이션이 종료되지 않으면 시스템이 이것을 강제로 종료합니다.

아이폰 어플리케이션은 백그라운드에서 동작하지 않기 때문에, 종료시 최종 데이터를 저장해야 다시 실행 했을 때 마지막으로 동작하던 모습을 재현해 낼 수 있습니다.


The Application Sandbox

아이폰 어플리케이션은 보안상의 이유로 파일시스템의 지정된 곳에만 위치할수 있다. 이것은 sandbox라는 보안 기능의 부분 기능입니다. sandbox 라는것은 보안 분야에서 사용되는 언어로, 모든 실행과 데이터를 시스템이 알 수 있는 범위에서 동작하도록 하고 어플리케이션의 모든 행위를 모니터링 할 수 있게 하는 개념입니다. 이것을 위해 아이폰 어플리케이션은 다음의 위치에서만 실행 될 수 있습니다.

/ApplicationRoot/Application ID/


하지만 아무리 sandbox로 동작한다고 해도 어필리케이션의 오버플로우나 기타 해킹 방법으로부터 자유로울 수는 없다는 것을 명심 하시기 바랍니다.


The Virtual Memory System

어플리케이션 메모리를 관리하기 위해 아이폰 OS는 Mac OS와 같은 virtual memory 시스템을 사용합니다. 다만 아이폰에서는 메모리가 부족할 때 디스크에 메모리 페이지를 쓰는 swap 기능을 사용하지 않습니다. 이는 빠른 동작을 위한 조치라고 판단됩니다. 대신 아이폰 OS는 현재 메모리에서 사용하지 않는 메모리의 코드 영역 같은 부분을 내려서 공간을 확보하며, 그래도 메모리가 부족하면 동작하고 있는 어플리케이션에 현재 필요없는 메모리를 해지하라는 메시지를 보냅니다. 어플리케이션은 이 메시지에 응답하여 cache나 불필요한 메모리를 해지할 의무가 있습니다.


The Automatic Sleep Timer

아이폰 OS에서는 베터리를 절약하기 위한 방법으로 자동 sleep timer 를 사용합니다. 사용자가 일정 시간동안 아이폰을 터치하지 않아서 이벤트가 발생되지 않는다면 아이폰 OS에서는 자동으로 시스템을 정지시키고 스크린을 끕니다. 하지만 게임이나 동영상 플레이어 같은 어플리케이션은 사용자 입력 없이도 오랜 시간 동작해야 할 수 있기 때문에 UIApplication 오브젝트의 idleTimerDisabled 속성을 YES로 만들어서 자동 slee timer를 끌 수 있습니다.


The Application Bundle

아이폰 어플리케이션을 빌드할 때, XCode는 Bundle과 함께 패키지 합니다. Bundle은 하나의 그룹화 되어 있는 하나의 디렉토리 입니다. 번들은 아래 내용과 같이 어플리케이션 아이콘, 프로그램 실행 파일 등 여러가지로 구성됩니다.

   MyApp - 실행 파일.Settings.bundle - 프로그램의 속성과 다른 리소스 파일에 대한 내용을 포함

Icon.png - 57 x 57 사이즈의 홈 스크린에서 보여지는 어플리케이션 아이콘

Icon-Settings.png - 29 x 29 사이즈의 어플리케이션이 동작할 때 어플리케이션 이름 옆에 나오는 아이콘

MainWindow.nib - 어플리케이션의 main nib 파일입니다. 이것은 어플리케이션의 main 윈도우 오브젝트와 delegate object 를 포함하고 있습니다. 다른 interface 오브젝트는 추가적인 nib 파일로 부터 로드하거나 소스 레벨에서 포함합니다. Info.plist 파일의 NSMainNibFile key 를 변경하여 이 파일 이름을 변경할 수 있습니다.

Default.png - 480 x 320 사이즈의 이미지로, 어플리케이션이 시작할 때 보이는 이미지 압니다.

iTunesArtwork - 512 x 512 사이즈의 이미지로, 어플리케이션을 배포할 때 보여지는 이미지 입니다. 이것은 appstore에 배포될 때 보여지게 됩니다.

Info.plist - 어플리케이션의 bundle ID, 버전 그리고 보여지는 어플리케이션 이름 등 어플리케이션의 주요 속성이 들어있습니다.

sun.png - 해당 언어가 지원되지 않을 때 보여지는 이미지입니다.

en.lproj, fr.lproj, es.lproj ... - 지원 언어 리소스 입니다.


The Information Property List

Info.plist 파일은 모든 아이폰 어플리케이션이 포함하고 있는 속성 파일입니다. 이것은 XCode의 Active Target TargetName 메뉴를 클릭하여 GUI로 수정할 수 있습니다.(그림 4)

사용자 삽입 이미지

그림 4. Active Target

여기서는 기본적인 이름등을 수정할 수 있고, 더 자세한 설정은 하단에 있는 Open Info.plist as File을 클릭하면 그림 5와 같은 화면이 실행되고 여기서 모든 속성을 수정할 수 있습니다.

사용자 삽입 이미지

그림 5. Info.plist 파일

Handling Critical Application Tasks

이 장에서는 어플리케이션이 꼭 수행해야 할 역할을 기술합니다.


Initialization and Termination

어플리케이션이 초기화되고 종료되는 동안 UIApplication 클래스는 해당 메시지를 전송합니다. 어플리케이션은 delegation 오브젝트에서 이것을 처리해야 합니다. 초기화와 종료를 처리하는것은 필수 사항은 아니지만, 어플리케이션의 초기화때 데이터를 초기화 하거나 종료할 때 데이터를 저장하는 등의 역할을 수행하기 때문에 대부분의 어플리케이션에서는 이 메시지를 이용해야 할 것입니다. 아이폰 어플리케이션은 시작과 종료가 빠르고 가볍게 처리되야 하기 때문에 이 메시지를 적절히 이용해야 하는데, 만약 어플리케이션이 네트워크로부터 데이터를 받아오거나 복잡한 연산을 하여 초기화가 느릴 때, 먼저 UI를 보여주고 그 후에 초기화를 처리하는 방법 등으로 작업을 해야 합니다.

다음 글 박스에서는 UIApplicationDelegate에서 초기화와 종료시에 사용하는 메소드 프로토콜을 설명합니다.

applicationDidFinishLaunching - 어플리케이션이 시작 될 때 실행되는 메소드 입니다.

applicationWillTerminate - 데이터를 저장하거나 어플리케이션의 상태를 저장합니다. 어플리케이션 종료 시 호출됩니다.


Responding to Interruptions

사용자가 홈 버튼을 클릭하여 프로그램을 종료시키는 것 이외에도, 시스템이 어플리케이션에 interrupt를 걸어서 사용자의 입력을 기다릴 수 있습니다. 예를 들어 전화가 오거나 SMS가 오는 경우에 사용자에게 다음 수행을 물어보고 전화를 받는다면 어플리케이션은 종료되게 됩니다. 그림 6은 이러한 동작을 표현합니다.

사용자 삽입 이미지

6. Interrption

1. 시스템이 전화나 SMS가 전송되거나 달력에서 이벤트가 발생하는 것을 탐지합니다.

2. Deligate의 applicationWillResignAcive를 호출합니다.

3. 시스템이 alert 패널을 띄워서 사용자에게 전화나 SMS 수신 등의, 해당 이벤트를 알립니다.

4. 만약 사용자가 해당 이벤트를 무시하면 applicationDidBecomeActive 를 호출합니다.

5. 사용자가 해당 이벤트의 수행을 지시하면 applicationWillTerminate 를 호출합니다. 이때 어플리케이션은 종료시와 마찬가지로 데이터나 어플리케이션 속성을 저장해야 할 것입니다.

아이폰 OS가 sleep 모드로 들어가거나 다시 깨어날 때도 applicationWillResignAcive와  applicationDidBecomeActive가 호출됩니다.


Observing Low-Memory Warnings

시스템이 low-memory notification을 어플리케이션에 전달하면 어플리케이션은 빠르게 필요없는 메모리를 해지해야 합니다. 이 notification을 알 수 있는 방법은 3가지가 있습니다.

  • applicationDidReceiveMeoryWarning 을 delegate에 구현합니다.
  • UIViewController의 didReceiveMeoryWarning 을 override 합니다.
  • UIApplicationDidReceiveMemoryWarningNotification 메시지를 등록하여 처리합니다.
Customizing Your Application's Behavior

이 장에서는 사용자의 경험에 기반하여 어플리케이션을 최적화 하는 방법을 제시합니다.


Launching in Landscape Mode

아이폰 어플리케이션은 기본적으로 potrait mode(세로)로 동작한다. 만약 portrait mode와 landscape mode 둘다 지원한다면 portrait mode 로 시작하는 것이 좋다. 만약 landscape 모드로만 동작하게 하려면 다음의 과정을 거쳐야 한다.

  • Info.plist 파일에 UIInterfaceOrientation 키를 추가하고, 여기에 landscape mode 를 세팅한다UIInterfaceOrientationLandscapeLeft(홈 버튼이 왼쪽 기준)또는 UIInterfaceOrientationLandscapeRight (홈 버튼이 오른쪽 기준) 속성 중 하나로 선택하면 된다.
  • 어플리케이션 UI를 landscape 모드에 맞게 만든다.
  • shouldAutorotateToInterfaceOrientation 을 landscape 모드에서는 YES를 리턴하게 하고, POTRAIT 모드에서는 NO를 리턴하게 한다.

Communicating with Other Applications


만약 어플리케이션이 시스템에 등록된 종류의 URL을 호출하면 시스템은 해당 URL에 해당하는 어플리케이션을 동작시킨다. 예를 들어 Map을 보여주는 URL을 실행하면 등록된 Google map 이 실행되는 것이다. 이것을 통해 어플리케이션간 통신을 수행 할 수 있습니다. 애플에서는 http(사파리), mailto(애플 기본 메일 클라이언트), tel, sms의 URL 기능을 기본적으로 제공합니다. 이 기능을 사용하기 위해서는 NSURL 오브젝트를 생성하여 UIApplication 오브젝트의 openURL 메소드를 사용하면 됩니다. 이를 통해 다른 어플리케이션이 실행되면 기존의 어플리케이션이 잠시 종료되고, 새롭게 실행된 어플리케이션이 종료되면 다시 자동으로 실행되게 됩니다.

다음은 다른 어플레이케이션에 데이터를 전송하는 예제 코드입니다.


NSURL *myURL = [NSURL
URLWithString:@"todolist://www.acme.com?Quarterly%20Report#200806231300"];
[[UIApplication sharedApplication] openURL:myURL];


위 코드를 보면 todolist 로 시작하는 URL을 사용하는 것을 볼 수 있는데, 이것은 아이폰 OS에 등록된 어플리케이션이 실행되면서 뒤의 URL을 전달하게 되는 것 입니다.


Implementing Custom URL Schemes


애플에서 기본적으로 세팅한 URL type은 변경할 수 없지만, 그 외의 URL type은 개발자가 별도로 등록할 수 있다. 이것을 수행하기 위해서는 Info.plist 파일에 CFBundleURLTypes 속성을 통해 등록합니다. 다음 글 박스는 이 속성의 값을 설명합니다.

CFBundleURLName - URL Type의 이름입니다. 이것은 어플리케이션별로 unique 해야 하기 때문에 DNS 를 거꾸로 표현한것 과 같은 com.acme.myscheme과 같은 방식을 사용하기를 권장합니다.

CFBundleURLSchemes - URL의 앞부분에 들어가는 이름을 문자열로 입력하며, 여러개를 사용할 수 있습니다. 예를 들어 todolist, dolist 등과 같이 등록하면 이 형식의 URL이 로드되면 해당 어플리케이션이 동작합니다.


이 기능을 테스트 하기 위해서는 safari를 사용하면 됩니다.


Handling URL Requests


다른 어플리케이션이 URL request를 통해 해당 어플리케이션을 동작시켰다면 이것은 delegate의  APPLICATION:handleOpenURL 메소드를 구현하여 처리할 수 있습니다. 이 메소드를 통해 입력되는 NSURL을 처리하여 동작시키면 됩니다.


Displaying Application Preferences


사용자가 바꾸는 설정이 바로 적용되거나 무엇인가 동작이 바뀌기를 기대하는 것은 어플리케이션 내부에 넣고, 그렇지 않은 것은 시스템의 설정에 넣으면 됩니다. 시스템에 넣는것은 Settings.bundle에 포함시켜서 설정할 수 있으며 Icon-Settings.png를 Bundle에 넣으면 시스템 설정창에 해당 아이콘이 보여지게 되며, 제공하지 않으면 기본적으로 어플리케이션 아이콘이 등록됩니다.


Internationalizing Your Application


텍스트, 이미지 등 모든 리소스는 사용하는 아이폰의 언어에 따라 다르게 보여지게 할 수 있습니다. 시스템에서 기본적으로 제공되는 alert나 기타 컨트롤들은 기본적으로 이 기능을 가지고 있습니다. 일반 어플리케이션에서 이 기능을 사용하려면 pInfo.list 에서 설정하여 사용할 수 있습니다. pInfo.list에서 lproj 폴더를 지정하여 해당 폴더에 코드가 생성하는 텍스트, 기본 텍스트, 아이콘, 사운드 파일, Nib 파일등을 넣을 수 있습니다.

lproj의 서브 디렉토리는 다음과 같습니다.

en.lproj/
- InfoPlist.strings / - 이 디렉토리에는 각 국가별 언어 디렉토리를 하위 디렉토리로 가지고 있습니다. 예를 들어 프랑스 언어는 fr.lproj 디렉토리를 통해 지원할 수 있습니다.
- Localizable.strings / - 어플리케이션이 생성하는 스트링이 포함됩니다.
- sign.png / - 국가별로 구분되는 이미지


NSLocalizedString 매크로를 이용하여 코드상에서 언어를 구분하여 지원할 수 있습니다.


NSString *NSLocalizedString(NSString *key, NSString *comment);

label.text = NSLocalizedString(@"City", @"Label for City text field");


NSLocalizedString은 key와 comment로 구성되어 있습니다. key는 언어를 말하며, 이것을 번역하기 위해 필요한 커맨드를 넣을 수 있습니다. 그 후 lproj 폴더에 다음과 같은 키를 Localizable.strings 파일에 넣으면 해당 언어로 번역됩니다.

"City" = "Ville";

다국어 지원에 대한 것은 더 자세히 팁 & 태크에서 소개해 드리겠습니다.

Tuning for Performance and Responsiveness


Using Memory Efficiently


메모리를 효과적으로 사용하기 위해서는 다음과 같은 사항을 지키시기를 권장합니다.
  • 메모리 릭을 최소화 해라
  • 리소스 파일을 최대한 작게 사용해라
  • 큰 데이터는 SQLite 를 이용하여 저장하고 불러와라.
  • 리소스는 필요할때만 불러와라.
  • Thumb 를 이용하여 컴파일하면 35%의 메모리가 절감된다. 하지만 그만큼 퍼포먼스는 떨어진다.

Allocating Memory Wisely

메모리를 할당할 때는 다음과 같은 사항을 지키시기를 권장합니다.
  • Autorelease 사용을 최대한 줄이고, 개발자가 직접 allocating 하고 release 해라.
  • 리소스를 줄여라.
  • 문제가 되는 메모리 사용을 회피해라.


Reducing Power Consumption


다음 기능의 사용의 최소화 하여야 베터리를 오래 쓸 수 있습니다.
  • Wi-fi radio
  • baseband cell radios
  • Core location framework (GPS)
  • Accelerometers (가속도 센서)
  • Disk
이로서 첫번째 챕터의 소개가 끝났습니다. 지금은 문서를 번역하고, 그에 따른 약간의 양념을 가미한 수준입니다. 하지만 앞으로 팁&태크나 새로운 강좌들을 통해 더욱 많은 소개 드리도록 하겠습니다. 아직까지 애플에서 제공되는 문서를 통해 포스팅을 하는 것은, 원문과 다르게 더 좋은 글을 쓰려고 하는것은 거의 불가능 하므로, 원문에 충실하게 계속 포스팅 하겠습니다.
Posted by Getroot

Leave your greetings.

  1. 정말 최고의 강좌네요. 원리를 알고 아이폰을 접근할 수 있어서 감사합니다. ^^

    2010/03/22 15:55 [ Permalink : Modify/Delete : Reply ]
  2. bellMT.

    좋은정보네요!~ 감사드립니다.

    2010/08/04 11:33 [ Permalink : Modify/Delete : Reply ]
  3. thanks for your posting

    2010/10/01 16:42 [ Permalink : Modify/Delete : Reply ]
  4. pkseop

    멋집니다. 정리하느라 수고 많이 하셨을듯 하네요.

    2010/12/06 20:13 [ Permalink : Modify/Delete : Reply ]