[iPhone] - 시작하세요! 아이폰 3 프로그래밍 - Part 10. 애플리케이션 설정과 사용자 기본값

iPhone/[위키북스]시작하세요! 아이폰3 프로그래밍 2010. 8. 10. 21:58
* 세팅 번들 이해하기
- Settings 애플리케이션은 어떤 애플리케이션이든지 세팅 번들이 있으면 사용자가 환경설정을 할 수 있게 해준다. 세팅 번들은 애플리케이션 안의 파일 묶음인데 그것은 Settings 애플리케이션에 어떤 환경설정을 사용자로부터 수집하고 싶은지 알려준다.

- 아이폰에서 사용자들이 기본값을 설정할 때 Settings 애플리케이션을 공용 사용자 인터페이스로 사용한다. 사용자가 기본설정은 애플리케이션 환경설정의 일부이고 설정을 저장하고 가져오는 역할을 한다. 사용자 기본설정은 NSUserDefaults클래스로 구현한다.

- 애플리케이션은 NSUserDefaults를 사용해서 환경설정 데이터를 키(Key)값을 통해 읽고 저장하는데 이것은 NSDictionary에서 키 값으로 데이터에 접근하는것과 똑같다. 맥과 다른 점은 NSUserDefaults 데이터는 메모리에 객체 인스턴스로 저장하는 것이 아니라 파일 시스템에 저장된다는 것이다.

- 애플리케이션에서 사용할 설정으로 프로퍼티 리스트를 만들면 Settings 애플리케이션이 인터페이스를 만들어 준다.

* 프로젝트 생성하기
- Xcode에서는 New Project를 선택한다. 새 프로젝트 도우미가 뜨면 왼쪽 창 iPhone밑의 Application를 선택하고 Utility Application아이콘을 클릭한다. 이름음 AppSettings이라고 한다.
- 이 프로젝트 템플릿을 살펴 보면 메인 뷰가 있고, 플립사이드(Flipside)뷰라고 부르는 보조 뷰가 있다. 메인 뷰의 정보 버튼을 탭하면 플립사이드 뷰가 나타나고, 플립사이드 뷰의 Done 버튼을 탭하면 다시 메인 뷰가 나타난다.

- 뷰 컨트롤러와 UIView의 하위클래스를 포함해서 메인 뷰를 구성하는 모든 클래스들은 Main View폴더안에 들어 있다. 플립사이드 뷰를 구현하는 데 필요한 모든 소스코드 파일은 Flipside View폴더에 있다. 애플리케이션 델리케이트는 Application Delegate폴더에 담겨 있다.

* 세팅 번들 다루기
- Settings 애플리케이션은 애플리케이션의 환경설정을 보여줄 때 그 애플리케이션 안의 세팅 번들을 기반으로 동작한다. 각 세팅 번들은 최상위 환경설정 뷰를 정의하는 Root.plist 프로퍼티 리스트를 포함해야 한다. 만약 적절한 Roop.plist 파일과 세팅 번들을 찾았다면 Settings 애플리케이션은 애플리케이션의 프로퍼팉 리스트 내용에 기반해서 설정 뷰를 만든다. 만약 설정에 하위뷰를 추가하고 싶다면, 추가 프로퍼티 리스트를 번들에 추가해야 하고 각 항목을 Root.plist에 추가해야 한다.

* 세팅 번들을 프로젝트에 추가하기
- AppSettings에서 새로운 파일을 추가 한다. 왼쪽 창에서 iPhone OS 밑의 Resource에서 Settings Bundle아이콘을 선택한다. 파일 이름은 Settings.bundle이다.

* 프로퍼티 리스트 설정하기
- Root.plist 파일을 보면 종류가 딕셔너리인 root 노드가 있는데, 이것은 NSDictionary처럼 값을 키 값을 사용해서 저장한다. 모든 딕셔너리 노드의 하위 항목은 키와 값이 필요하다. 그리고 주어진 프로퍼티 리스트에 root 노드는 하나만 있어야 하고 모든 노드는 그 밑에 있어야 한다.

- 첫번째 항목인 StringsTable은 애플리케이션의 언어를 다른 언어로 교체 할때 사용된다. 이번 장에서는 삭제....

- Root 노드의 다음 항목은 Type이 배열인 PreferenceSpecifiers이다. 이 배열의 각각은 설정 항목이나 사용자가 하위 항목으로 들어갈 수 있는 하위뷰를 나타낸다.

- Item 0을 클릭하지만 펼치지는 말자. 행의 오른쪽 모서리를 보면 더하기(+)아이콘이 있다. 이 버튼은 이행 다음에 형제 노드를 추가한다. 다시 말해 다른 노드를 이 노드와 같은 레벨에 추가한다.

- 이제 Item 0을 펼쳐 보는데 이때 더하기 버튼이 가로로 세 줄이 있는 다른 아이콘으로 바뀐다. 하위 노드를 추가 할때 가로 세줄 아이콘의 버튼을 클릭하면 된다.

- Item 0 아래의 첫번째 행은 Key가 Type인데 PreferenceSpecifiers 배령에 있는 모든 프로퍼티 리스트노드에는 Key가 Type인 항목이 있어야 한다. Type키는 Settings 애플리케이션이 이 아이템과 관련된 어떤 데이터 타입을 사용해야 하는지를 알게 해준다.

- Value을 PSGroupSpectifier로 변경한다. PSGroupSpecifier 타입 필드는 새 그룹의 시작 부분에 아이템을 나타내는데 사용한다. 각각의 아이템들은 PSGroupSpecifier 타입의 아이템이 나올때 까지 이 그룹의 일부를 따를것이다. Settings 애플리케이션은 설정 항목을 그룹화된 테이블로 제공한다. 그러므로 모든 테이블에 최소한 하나의 그룹은 있어야 하기 때문에 세팅 번들 프로퍼티 리스트의 PreferenceSpecifiers 배열 안에 있는 Item 0는 항상 PSGroupSpecifier로 설정해야 한다.
(개인적 생각 책의 말이 어려운데.. Settings 애플리케이션은 그룹회된 테이블로 제공하기 때문에 다음 PSGroupSpecifier가 나오기 전까지는 현재에 그룹에 묶으라는 의미 인거 같다.)

- Item 0에 있는 또 다른 항목의 Key는 Title이고 이것은 그룹화된 테이블용 헤더로 쓰인다. 값을 General Info로 변경한다.

* 텍스트 필드 설정 추가 하기
- Item 0의 펼쳐보기 삼각형을 접은 다음 Item 0을 선택하고 행 끝의 더하기 버튼을 클릭한다.

- 새 행의 Type은 기본값이 String으로 되어 있는데 PreferenceSpecifiers 배열에 있는 각 항목은 Dictionary여야 하기 때문에 String을 클릭하고 Dictionary 타입으로 바꾼다.

- 이제 Item 1의 펼쳐보기 삼각형을 클릭하고 새로운 행을 추가 한다.

- 기본 Type이 String인 새 행이 추가된다. 새 행에 있는 key의 기본 값은 New item인데 값을 Type으로 바꾸고 Value 값은 PSTextFieldSpecifier로 입력한다. Settings 애플리케이션에서 사용자가 텍스트 필드를 사용해서 설정값을 수정하도록 한다.

- 새로운 행을 추가 하고 텍스트 필드의 레이블을 설정한다. New item인 key를 Title로 변경하고 Value은 Username으로 입력

- 새로운 행을 또 추가 하고 Key를 Key로 입력한다. Value은 username이라고 입력한다. 현재 항목은 어떤 키를 사용해서 텍스트 필드에서 값을 입력받을지 Settings 애플리케이션에게 알려주는 역할을 한다. NSUserDefaults는 NSDictionary처럼키를 사용해서 사용자가 값을 저장하게 해준다.나중에 username에 대한 값을 요청할수도 있다.

- 새로운 행을 추가 하고 Key가 AutocapitalizationType고 Value은 None인 항목을 추가 한다. 이렇게 하면 사용자가 입력할 때 텍스트 필드가 자동으로 첫 글자를 대문자로 바꾸려 하지 않도록 설정한다.

- 마지막으로 새로운 행을 추가 하고 Key가 AutocorrectionType으로 하고 Value는 No로 한다. 이렇게 하면 텍스트 필드에 값을 입력할 때 Settings 애플리케이션이 자동으로 값을 정정하지 않는다.

- 이제 실행... GOGO


* 암호화된 텍스트 필드 설정 추가하기
- 새로운 노드를 쉽게 추가하는 방법을 알려주겠다. PreferenceSpecifiers배열의 Item 1를 접는다. 이제 Item 1를 선택하고 Func키 + C를 눌러 클립도으에 복사한 다음 Func + V를 눌러 다시 붙여 넣는다.

- 새 항목을 추가 하고 Key는 IsSecure로 하고 Type은 Boolean으로 바꾼다. 해당 체크 박스를 체크 한다. 이제 이 필드는 일반 텍스트 필드가 아니라 암호화된 필드인다.


* 멀티밸류 필드 추가하기
- 다음으로 추가할 항목은 멀티밸류 필드이다. 이런 종류의 필드는 더보기 표시가 있는 행을 자동으로 생성해주고 그 행을 클릭하면 다른 테이블 뷰가 나타나 여러 행 중에서 하나의 값을 선택하게 한다.

- Item 3를 추가한다. Item 3의 타입을 Dictionary로 바꾸고 펼쳐보기 삼각형을 클릭해서 Item 4를 펼친다.

- Key가 Type이고 Value가 PSMultiValueSpecifier인 하위 행을 만든다. 두번째 행은 Key를 Title로, Value를 Protocol로 생성한다. Key가 Key이고 Value가 protocol인 세번째 행을 추가한다.

- Item 3에 Array 타입 노드를 하위 항목으로 두개 더 추가 할것이다. 그 중 하나는 Titles이고 여기에는 사용자가 선택할 값을 넣는다.(사용자가 선택할때 보게 되는 값)

- 다른 하나인 Values는 사용자가 기본값으로 실제 저장될 값의 목록이다. 즉 사용자가 목록에서 Titles 배열의 첫 번째 아이템에 해당하는 항목을 선택하면 Settings 애플리케이션은 Values배열의 첫 번째 값을 실제로 저장한다.

- 이 Titles와 Values의 상으로 사용자들이 편하도록 타이틀을 보여주지만 실제 저장할 때는 숫자나, 날짜, 다른 문자열처럼 다른 값을 저장 한다. 두 배열 모두 필수 있다.

- Item 3에 새 하위 노드를 추가한다. Key를 Values로 바꾸고 타입을 Array로 한다. 배열을 펼치고 다섯개의 하위 노드를 추가한다. 노드 다섯 개의 타입은 전부 String이고 HTTP, SMTP, NNTP, IMAP, POP3 값을 가진다.

- 다섯 개를 입력하고 나면 Values를 접은 후 이항목을 선택한다. 그런 후에 Func + C를 눌러서 복사하고 Func + V를 눌러서 다시 복사한다. 이렇게 하면 Key가 Values-2인 새항목이 생긴다. Values-2를 Titles로 바꾼다.

- 딕셔너리에 기본값으로 사용할 필수 값 하나가 더 필요하다. 멀티밸류 필드는 꼭 하나의 행이 선택되어야 하므로 아무것도 선택하지 않았을 때의 기본값을 지정해야 하는데 기본값은 Values 배열 중 하나의 값이어야 한다. Item 3에 하위 노드를 추가한다. Key를 DefaultValue로, Value는 SMTP로 한다.


* 토글 스위치 세팅 추가하기
- 사용자에게서 얻어야 할 다음 항목은 워드(Warp) 엔진이 켜져 있는지를 나타내는 Boolean 값이다.

- Settings 애플리케이션의 PreferenceSpecifiers 배열에 타입이 PSToggleSwitchSpecifier인 항목을 추가한뒤 UISwitch를 이용해서 Boolean 값을 얻을 것이다.

- Item 4를 추가한다. 하위 행의 Key를 Type으로, 값은 PSToggleSwitchSpecifier로 한다. 또 다른 행을 추가해서 Key는 Title로, 값은 Wrap Drive로 입력한다. 다음으로 Key가 Key이고 Value가 wrap인 세 번째 행을 추가한다.

- 기본적으로 토글 스위치는 사용자 설정값으로 YES나 NO인 Boolean 값을 가진다. TrueValue / FalseValue키를 추가해서 on / off 위치에 다른 값을 할당할 수 있는데 이들 값이 필수는 아니다.

- Key가 TrueValue이고, Value가 Engaged인 행과 Key가 FalseValue이고, Value가 Disabled인 행을 추가해서 Boolean 값 대신 다른 값을 저장할 수 있게 한다.

- 이 딕셔너리에 기본 값 설정이 하나 더 필요하다. 만약 TrueValue와 FalseValue 행이 없다면 Key가 DefaultValue인 새행을 추가하고 타입을 String에서 Boolean으로 바꾼다. 그러나 두 행을 추가 했으니 TrueValue나 FalseValue의 값 중 하나를 DefaultValue의 Value로 입력한다.

- Key가 DefaultValue, Value를 Engaged로 한다. 문자열 "Engaged"는 사용자 기본값으로 저장되는 값이고 화면에 나타나는 것은 아니다.


* 슬라이더 세팅 추가하기
- Settings 애플리케이션에서 슬라이더는 양 끝쪽에 작은 이미지가 있지만 레이블이 없다. 레이블이 있는 슬라이더용 그룹을 만들고 거기에 슬라이더를 넣어서 사용자가 슬라이더가 무엇을 하는지 알 수 있도록 한다.

- 새로운 행을 하나 추가 하고 Type을 Dictionary로 바꾼다. 추가된 행은 Item 5가 된다.

- 새로운 2개의 하위 행을 생성한다. Key는 Type으로 하고 Value은 PSGroupSpecifier로 설정한다. Key가 Title이고, Value이 Warp Factor인 행을 추가한다. 이 행은 새로운 그룹을 추가하기 위해서 세팅하는것이다.

- 새로운 행을 하나 추가한다. Type을 Dictionary로 바꾼다. 추가된 행은 Item 6가 된다.

- Key가 Type이고 Value가 PSSliderSpecifier인 하위 노드를 추가 한다. 이렇게 하면 UISlider를 사용해서 사용자로부터 정보를 얻는다는 사실을 Setting 애플리케이션에서 알게 된다. Key가 Key이고 Value가 warpfactor인 새 행을 추가해서 이 값을 어떤 키에 저장할지 Settings 애플리케이션이 알게 된다.

- 사용자들이 1에서 10 사이의 값을 입력하도록 하고 기본값은 5로 설정하려 한다. 슬라이더는 최소값, 최대값, 기본 값이 필요하고, 문자열이 아닌 숫자로 저장해야 한다. Item 6에 새개의 하위 행을 추가 해서 이 값을 설정하는데 이 행의 Type을 모두 String에서 Number로 바꾼다. 첫번째 Key를 DefaultValue로 하고 Value는 5를 준다. Key가 MinimumValue이고 Value가 1인 행을 두 번째로 추가하고 마지막 행은 Key를 MaximumValue로 Value은 10으로 입력한다.

- 크기가 21 픽셀 * 21 픽셀인 이미지를 슬라이더의 양 끝에 둘수 있다.

- 이미지 둘 모두 세팅 번들에 추가해야 한다. 그렇게 하려면 파인더를 사용해서 Xcode프로젝트를 저장한 폴더를 열어야 한다. 그 폴더에서 이름이 Setting.bundle인 아이콘을 찾을 수 있다.

- 파인더에서 번들은 파일처럼 보이지만 실제로는 폴더이다. 번들의 내용을 보기 위해 버튼의 아이콘을 마우스 오른쪽 버튼으로 클릭하고 Show Package Contents를 선택한다. 이렇게 하면 새창이 뜨고 Xcode의 Setting.bundle에 있는 것과 같은 항목 두 개가 보인다. 10 AppSettings 폴더의 아이콘 파일 rabbit.png와 turtle.png를 이 폴더에 복사한다.

- Item 6에 하위 행 2개를 추가 한다. 첫번째 행은 Key를 MinimumValueImage로, Value로 turtle.png로 한다. 두 번째 행의 Key는 MaximumValueImage, Value는 rabbit.png로 한다.

* 하위 설정 뷰 추가하기
- 또 다른 설정 지정자를 추가해서 Settings 애플리케이션에서 하위 설정 뷰로 넘어갈 수 있게 할것이다. 이렇게 하면 더보기 펴시가 있는 행이 나타나고 탭하면 화면 전체가 설정인 새 뷰가 나타난다.

- 새로운 행 하나를 추가 한다. 행 이름은 Item 7이 되고 Type은 Dictionary로 변경한다. 새로운 Group을 생성하기 위해서 2개의 하위 뷰를 추가한다. Key를 Type으로 설정하고 Value은 PSGroupSpecifier로 한다. 새로운 하위 행의 Key는 Title로 설정하고 Value은 Additional Info로 설정한다.

- 새로운 행 하나를 추가 한다. 행 이름은 Item 8이 되고 Type은 Dictionary로 변경한다. 하위 행을 추가하고 Key를 Type으로, Value을 PSChildPaneSpecifier로 입력한다. Key가 Title, Value가 More Settings인 또 다른 하위 행을 추가한다.

- 마지막 행을 추가해서 Settings 애플리케이션이 More Settings 뷰를 보여줄 때 어떤 프로퍼티 리스트를 로드할지 알려준다. Key를 File로, Value는 More로 입력한다. 파일 확장자가 .plisit라면 꼭 추가할 필요는 없지만 확장자가 다를 때는 확장자를 입력하지 않으면 Settings 애플리케이션이 프로퍼티 리스트 파일을 찾지 못할 수도 있다.

- 메인 설정 뷰에 하위뷰를 추가하였다. 하위뷰의 설정 항목들은 More.plist 파일에 명시되어 있다. More.plist의 내용을 설정 항목 번들에 복사해야 한다. Xcode에서 새 파일을 번들에 추가할 수 없고 프로퍼티 리스트 에디터의 저장 창에서도 번들에 저장할 수 없다. 그래서 어딘가 다른 곳에서 새 프로퍼티 리스트를 만들어서 저장하고 파인더를 사용해서 Setting.bundle창에 끌어다 놓아야 한다.


* 애플리케이션에서 설정 읽기

- NSUserDefaults 클래스를 사용해서 사용자의 설정을 읽을 것이다. NSUserDefaults는 싱글톤패턴으로 구현되어서 실행 중인 애플리케이션에 오직 하나의 인스턴스만 존재한다. 이 인스턴스에 접근한려면 클래스 메서드인 standardUserDefults를 다음과 같이 호출한다.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

- NSUserDefaults의 인스턴스를 한번 얻고 나면 NSDictionary처럼 사용할 수 있다. 설정값을 얻으려면 objectForKey: 메서드를 호출하면 되고 이 메서드는 NSString, NSDate, NSNumber 같은 오브젝트 C객체를 리턴한다. 만약 int, float, BOOL 같은 스칼라 값을 얻으려면 initForKey: floatForKey: boolForKey: 같은 다른 메서드를 사용하면 된다.
- 사용자의 설정 항목과 연결된 모든 설정 지정자에는 Key라는 이름의 키가 있다. 사용자 설정값을 가져올 때 이 키를 사용한다.

#### MainViewController.h ####
#import "FlipsideViewController.h"

#define kUsernameKey @"username"
#define kPasswordKey @"password"
#define kProtocolKey @"protocol"
#define kWarpDriveKey @"warp"
#define kWarpFactorKey @"warpFactor"
#define kFavoriteTeaKey @"favoriteTea"
#define kFavoriteCandyKey @"favoriteCandy"
#define kFavoriteGameKey @"favoriteGame"
#define kFavoriteExcuseKey @"favoriteExcuse"
#define kFavoriteSinKey @"favoriteSin"

@interface MainViewController : UIViewController  {
	UILabel *usernameLabel;
	UILabel *passwordLabel;
	UILabel *protocolLabel;
	UILabel *warpDriveLabel;
	UILabel *warpFactorLabel;
	
	UILabel *favoriteTeaLabel;
	UILabel *favoriteCandyLabel;
	UILabel *favoriteGameLabel;
	UILabel *favoriteExcuseLabel;
	UILabel *favoriteSinLabel;	
}

@property (nonatomic, retain) IBOutlet UILabel *usernameLabel;
@property (nonatomic, retain) IBOutlet UILabel *passwordLabel;
@property (nonatomic, retain) IBOutlet UILabel *protocolLabel;
@property (nonatomic, retain) IBOutlet UILabel *warpDriveLabel;
@property (nonatomic, retain) IBOutlet UILabel *warpFactorLabel;

@property (nonatomic, retain) IBOutlet UILabel *favoriteTeaLabel;
@property (nonatomic, retain) IBOutlet UILabel *favoriteCandyLabel;
@property (nonatomic, retain) IBOutlet UILabel *favoriteGameLabel;
@property (nonatomic, retain) IBOutlet UILabel *favoriteExcuseLabel;
@property (nonatomic, retain) IBOutlet UILabel *favoriteSinLabel;

-(void)refreshFields;
-(IBAction)showInfo:(id)sender;

@end

- MainView.xib를 더블클릭해서 인터페이스 빌더에서 연다. 창이 뜨면 뷰의 배경색이 어두운 회색이라는 것을 발견할 것이다. 이 배경색을 흰색으로 바꿀 것이다. Nib 메인 창의 MainView 아이콘을 클릭하고 Func + 1를 눌러서 속성 인스펙터를 띄운다. Background 항목의 색 선택 창을 사용해서 배경을 흰색으로 바꾼다.
- MainView.xib 파일에서 View Mode를 리스트 모드로 변경한다. View 아이콘의 왼쪽에 있는 삼각형 아이콘을 클릭하면 Light Info Button라는 아이콘을 볼수 있다. Light Info Button 아이콘을 선택하고 Func+1를 눌러 속성 인스펙터를 띄운다. 버튼의 Type을 Info Light에서 Info Dark로 변경한다.
- 이제 레이블들을 Main View에 추가하여 아래 그림처럼 총 20개의 레이블을 설정한다. Text값이 없어서 그렇지 레이블 옆에 또 하나의 레이블이 있다. 그 후에 File's Owner에서 각 레이블로 컨트롤을 누르면서 끌어놓아서 레이블에 설정값이 출력되도록 한다.


#### MainViewController.m ####
@implementation MainViewController

@synthesize usernameLabel;
@synthesize passwordLabel;
@synthesize protocolLabel;
@synthesize warpDriveLabel;
@synthesize warpFactorLabel;
@synthesize favoriteTeaLabel;
@synthesize favoriteCandyLabel;
@synthesize favoriteGameLabel;
@synthesize favoriteExcuseLabel;
@synthesize favoriteSinLabel;

// refreshFields메서드는 프로퍼티 파일에 있는 키 값을 사용해서 사용자 설정값을 읽어 해당하는
// 레이블의 텍스트 프로파티를 설정한다.
-(void)refreshFields
{
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	usernameLabel.text = [defaults objectForKey:kUsernameKey];
	passwordLabel.text = [defaults objectForKey:kPasswordKey];
	protocolLabel.text = [defaults objectForKey:kProtocolKey];
	warpDriveLabel.text = [defaults objectForKey:kWarpDriveKey];
	// 슬라이더의 설정값은 NSNumber 객체를 가져오는데 여기서는 stringValue를 호출해서 객체의 값을 문자열로 표현한 문자열을 가져온다.
	warpFactorLabel.text = [[defaults objectForKey:kWarpFactorKey] stringValue];
	
	favoriteTeaLabel.text = [defaults objectForKey:kFavoriteTeaKey];
	favoriteCandyLabel.text = [defaults objectForKey:kFavoriteCandyKey];
	favoriteGameLabel.text = [defaults objectForKey:kFavoriteGameKey];
	favoriteExcuseLabel.text = [defaults objectForKey:kFavoriteExcuseKey];
	favoriteSinLabel.text = [defaults objectForKey:kFavoriteSinKey];
}

......

// viewDidAppear는 코드에서 서브뷰로 추가될 때만 호출된다.
// viewDidAppear - 이 메서드는 뷰 컨트롤러가 관리하는 각각의 뷰가 뷰 사이의 네비게이션에 의해
// 화면에 완전히 나타난 다음 호출된다. 최초로 뷰가 나타날 때도 호출된다.
// viewDidDisappear - 이 메서드는 viewDidAppear: 와 반대로 뷰 컨트롤러가 관리하는
// 각각의 뷰가 뷰 사이의 네비게이션에 의해 화면에서 사라진 후에 호출된다.
-(void)viewDidAppear:(BOOL)animated
{
	[self refreshFields];
	[super viewDidAppear:animated];
}

.......

// 사용자가 몇가지 항목을 설정할 수 있는 맞은편 뷰를 종료하면 컨트롤러 클래스에서 이벤트가 전달될 것이다.
// 이벤트가 전달될 때 변경된 내용을 보여줄 수 있도록 레이블을 업데이트해야 한다. 아래 메서드가 이벤트를 전달 받는다.
// 반대편 뷰는 자신의 모달 부모인 메인뷰와 함께 모달의 형태로 처리되기 때문에 반대편 뷰가 소멸될때 
// MainViweController인 viewDidAppear:메서드는 호출되지 않는다 그래서 refreshFields:를 이 메서드에 추가한다.
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {
    [self refreshFields];
	[self dismissModalViewControllerAnimated:YES];
}

......

- (void)viewDidUnload 
{
	self.usernameLabel = nil;
	self.passwordLabel = nil;
	self.protocolLabel = nil;
	self.warpDriveLabel = nil;
	self.warpFactorLabel = nil;
	self.favoriteTeaLabel = nil;
	self.favoriteCandyLabel = nil;
	self.favoriteGameLabel = nil;
	self.favoriteExcuseLabel = nil;
	self.favoriteSinLabel = nil;
	[super viewDidUnload];
}

.....

- (void)dealloc {
	[usernameLabel release];
	[passwordLabel release];
	[protocolLabel release];
	[warpDriveLabel release];
	[warpFactorLabel release];
	[favoriteTeaLabel release];
	[favoriteCandyLabel release];
	[favoriteGameLabel release];
	[favoriteExcuseLabel release];
	[favoriteSinLabel release];
    [super dealloc];
}

.....


* 애플리케이션에서 설정 바꾸기
- 여기서는 Settings애플리케이션이 사용하는 스위치, 슬라이더와 똑같은 컨트롤을 사용할 것이다.

####  FlipsideViewController.h ####
#import 

@protocol FlipsideViewControllerDelegate;


@interface FlipsideViewController : UIViewController {
	id  delegate;
	UISwitch *engineSwitch;
	UISlider *warpFactorSlider;
}

@property (nonatomic, assign) id  delegate;
@property (nonatomic, retain) IBOutlet UISwitch *engineSwitch;
@property (nonatomic, retain) IBOutlet UISlider *warpFactorSlider;

- (IBAction)done:(id)sender;
@end


@protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
@end

- 이제 FlipsideView.xib를 더블클래서 인터페이스 빌더에서 연다. 속성 인스펙터에서 배경 색을 밝은 회색 그림자로 변경한다. 회색을 25%로 설정하면 될 것이다. 다음으로, 라이브러리에서 Labels 두 개를 끌어서 Flipside View창에 놓는다. 레이블 중 하나를 더블클릭해서 타이틀을 Warp Engines:로 바꾸고 다른 레이블을 더블 클릭해서 타이틀을 Warp Factor: 라고 입력한다.
- 뷰의 상단에 있는 Title을 더블클릭하고 Warp Settings로 변경한다.
- Switch를 라이브러리에서 끌어서 Warp Engines라고 쓰인 레이블 오른쪽에 놓는다. File's Owner 아이콘에서 컨트롤을 누른 채로 클릭해서 새 스위치까지 끌어다 놓고, engineSwitch 아웃렛에 연결한다.
- Slider를 라이브러리에서 끌어서 Warp Factor레이블 밑에 놓는다. 슬라이더를 왼쪽과 오른쪽의 여백에 있는 파란 안내선까지 늘려서 슬라이더의 크기를 조절하고, File's Owner 아이콘에서 슬라이더로 컨트롤을 누른 채로 끌어서 warpFactorSlider 아웃렛에 연결한다.
- 슬라이더를 클릭하고 Func+1을 눌러서 속성 인스펙터를 띄운다. Minimum을 1.00으로, Maximum은 10.0으로, Initial은 5.00으로 설정한다. 다음으로 Min Image의 값으로 trurtle.png를 선택하고, Max Image의 값은 rabbit.png를 선택한다.

#### FlipsideViewController.m ####
#import "FlipsideViewController.h"
#import "MainViewController.h"

@implementation FlipsideViewController

@synthesize delegate;
@synthesize engineSwitch;
@synthesize warpFactorSlider;

- (void)viewDidLoad {
    
	//self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	// 워프 드라이버를 설정할 때 Bollean 대신 문자열을 사용했고 UISwitch의 인스턴스는 BOOL 프로퍼티로
	// 설정하기 때문에 문자열을 Boolean으로 변환할 필요가 있다.
	engineSwitch.on = ([[defaults objectForKey:kWarpDriveKey] isEqualToString:@"Engaged"]) ? YES : NO;
	warpFactorSlider.value = [defaults floatForKey:kWarpFactorKey];
	
	[super viewDidLoad];
}

// 메인 뷰로 다시 돌아오기 전에 사용자 설정을 제대로 반영할 수 있게 하려고 viewWillDisappear:메서드도 오버라이드 했다.
// 왜냐하면 메인 뷰의 viewWillAppear:메서드가 호출되기 전에 컨트롤러의 viewDidDisappear:메서드가 
// 실행되기 때문에 바뀐 설정값을 가져와서 제대로 된 새 값을 뷰에 반영한다.
-(void)viewWillDisappear:(BOOL)animated
{
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
	NSString *prefValue = (engineSwitch.on) ? @"Engaged" : @"Disabled";
	[defaults setObject:prefValue forKey:kWarpDriveKey];
	[defaults setFloat:warpFactorSlider.value forKey:kWarpFactorKey];
	[super viewWillDisappear:animated];
}

....

- (void)viewDidUnload {
	self.engineSwitch = nil;
	self.warpFactorSlider = nil;
	[super viewDidUnload];
}

- (void)dealloc {
	[engineSwitch release];
	[warpFactorSlider release];
    [super dealloc];
}

: