등록신청하고 2주정도만에 겨우 완료했습니다. 단계마다 오류가 있어 애플과 몇번의 메일을 주고 받다가 오늘 무사히 등록했습니다. 등록후에는 아이폰 개발자 포탈에서 iPhone Developer Program Protal User Guide란 문서를 다운로드 받아 설명한대로 진행하니 별다른 어려움 없이 개발환경을 완료할 수 있었습니다.

편하게 사용할려고 App ID 생성시에 번들아이디를 와일드카드(*)로 지정했는데, 터치로 컴파일시에는 문제가 없었습니다. 배포시에는 문제가 될지 모르겠지마, 다시 생성하면 되니 일단은 저 App ID 하나로 쓰기로 했습니다.


모든 인증 완료 후에 아이팟 터치를 아이튠즈에서 동기화를 하고나니, 아래와 같이 설정/일반에 프로파일 항목이 추가되어 있습니다.


테스트를 해볼려고 Xcode를 실행하니 아래와 같은 오류가 났습니다.

SDK를 다시 설치할려다가 자료를 찾아 보니 아이팟 터치 2세대로 개발을 할 때는 아래와 같이 심볼릭 링크를 해주어야 한다고 합니다. 아래와 같이 심볼릭링크를 생성하니 위의 오류 메시지는 없어졌습니다.

> cd /Developer/Platforms/iPhoneOS.platform/DeviceSupport
> ln -s 2.1 2.1.1

프로젝트의 빌드 설정에서 아래와 같이 iPhone Developer를 제 이름으로 설정하고 Profile을 설정하고, 빌드를 하니 제 아이팟 터치에도 잘 올라가고 실행도 잘되네요.


이제는 뭘 만들어서 올리는 일만 남았는데, 뭘 만들어야 할지 모르겠네요.
AND

PickerView, ImageView, TableView를 이용한 간단한 예제입니다. 좌측의 테이블뷰나 하단의 피커뷰에서 moveIn, push, reveal, fade 효과를 선택하면 우측의 이미지가 해당 효과로 다음 이미지와 변환되는 간단한 샘플입니다. 피커뷰의 2번째 컴퍼넌트에서는 효과가 진행되는 방향을 설정합니다. fade 효과에서는 이 방향이 적용되지 않습니다.

(사용한 샘플 이미지에 대해선 사과 드립니다)

이 샘플은 애플의 아이폰 DevCenter의 샘플코드중에서 UICatalogView Transitions 를 참조하였습니다.

1. 프로젝트 생성

Xcode의 메뉴에서 File / New Project를 클릭합니다. iPhone OS / Application을 선택하고 View-Based Application 템플릿을 선택하고 Choose..를 클릭합니다.


적당한 프로젝트명을 입력하고 Save를 클릭하여 프로젝트를 생성합니다. 여기서는 Control2로 생성하였습니다.


2. 소스코드 수정
Control2ViewController의 소스와 헤더 파일에 아래의 푸른색으로 되어 있는 부분을 입력합니다. 설명은 간단한 주석으로 대체하겠습니다.

1) Control2ViewController.h
#import <UIKit/UIKit.h>

@interface Control2ViewController : UIViewController {

    IBOutlet UITableView *myTableView;
    IBOutlet UIPickerView *myPickerView;
    IBOutlet UIView *myView;
   
    UIImageView *imageView1;
    UIImageView *imageView2;
   
    NSArray *effectArray;
    NSArray *directionArray;
}

@end

2) Control2ViewController.m
#import "Control2ViewController.h"
#import <QuartzCore/QuartzCore.h>

@implementation Control2ViewController

static NSString *kCellIdentifier = @"MyIdentifier";
static NSString *kAnimationKey = @"transitionViewAnimation";

- (void)viewDidLoad {
    [super viewDidLoad];
   
    // 에니메이션 효과들을 배열로 저장
    effectArray = [[NSArray arrayWithObjects:kCATransitionMoveIn, kCATransitionPush,
                    kCATransitionReveal, kCATransitionFade, nil] retain];
   
    // 에니메이션 시 진행방향을 배열로 저장
    directionArray = [[NSArray arrayWithObjects:kCATransitionFromLeft, kCATransitionFromRight,
                       kCATransitionFromTop, kCATransitionFromBottom, nil] retain];
   
    // 이미지 뷰 생성
    imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"no1.png"]];
    imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"no2.png"]];
   
    // 이미지 뷰 크기 설정
    CGRect rect;
   
    rect.origin = CGPointZero;
    rect.size = [myView frame].size;
       
    [imageView1 setFrame:rect];
    [imageView2 setFrame:rect];

    // 첫번째 이미지 뷰 추가
    [myView addSubview:imageView1];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [imageView1 release];
    [imageView2 release];
   
    [effectArray release];
    [directionArray release];
   
    [super dealloc];
}

#pragma mark Animation methods

/** 에니메이션 실행 */
- (void)transitionView:(NSUInteger)effect
{
    UIView* newView;
    UIView* oldView;
   
    // 나올 뷰와 사라질 뷰를 설정
    if ([imageView1 superview] == myView) {
        newView = imageView2;
        oldView = imageView1;
    } else {
        newView = imageView1;
        oldView = imageView2;
    }
   
    // 이전 뷰를 삭제하고 새로운 뷰를 등록
    [oldView removeFromSuperview];
    if (newView && ([newView superview] == nil)) {
        [myView insertSubview:newView atIndex:1];
    }
   
    // 에니메이션 효과와 방향을 설정
    NSString *transition = [effectArray objectAtIndex:effect];
    NSString *direction = [directionArray objectAtIndex:[myPickerView selectedRowInComponent:1]];
   
    // 에니메이션
    CATransition *animation = [CATransition animation];
   
    // 변환효과 설정
    if (transition == kCATransitionFade) {
        [animation setType:kCATransitionFade];
    } else {
        [animation setType:transition];
        [animation setSubtype:direction];
    }
   
    // 변환속도 설정
    [animation setDuration:0.75];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
   
    [[myView layer] addAnimation:animation forKey:kAnimationKey];
}   


#pragma mark TableView delegate/dataSource methods

/** 테이블뷰의 선택이 변경되었을 경우 */
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // PickerView의 효과 컴포넌트 선택 변경
    [myPickerView selectRow:[indexPath row] inComponent:0 animated:TRUE];
   
    // 에니메이션 실행
    [self transitionView:[indexPath row]];
}

/** 테이블뷰 섹션 수 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

/** 테이블뷰 row count */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [effectArray count];
}

/** 테이블뷰 각 항목 타이틀 설정 */
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:kCellIdentifier] autorelease];
    }
   
    cell.text = [effectArray objectAtIndex:indexPath.row];
   
    return cell;
}

/** 색션 해더 타이틀 설정 */
- (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section
{
    return @"Effect";
}


#pragma mark PickerView delegate/dataSource methods

/** 피커뷰의 선택이 변경되었을 경우 */
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    // componet가 0(첫번째 컴퍼넌트)일 경우에만 처리
    if (component == 0) {
        // 테이블뷰의 선택된 row 변경
        [myTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:component]
                                 animated:NO
                           scrollPosition:UITableViewScrollPositionTop];
   
        // 에니메니션 실행
        [self transitionView:row];
    }
}

/** 피커뷰의 컴퍼넌트 수 설정 - 2(효과, 방향) */
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 2;
}

/** 피커뷰의 각 컴퍼넌트의 row count */
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    if (component == 0)
        return [effectArray count];
    else
        return [directionArray count];
}

/** 피커뷰의 각 컴퍼넌트의 항목 타이틀 설정 */
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    if (component == 0)
        return [effectArray objectAtIndex:row];
    else
        return [directionArray objectAtIndex:row];
}

@end


3. 인터페이스 빌더

이제 인터페이스 빌더에서 UI를 생성하고 속성들을 연결해 보겠습니다.

Groups & Files의 Resources 폴더에서 Control2ViewController.xib를 더블클릭하여 인터페이스 빌더를 오픈 합니다.

1) 컨트롤 배치
라이브러리 윈도우에서 View, TableView, PickerView를 각각 드래그 해서 아래와 같이
View에 배치합니다.

2) Outlet 연결
xib 윈도우에서 File's Owner를 우클릭 합니다. 여기서 File's Owner는 Control2ViewController 입니다. 각 Outlet들을 위에서 생성한 컨트롤들과 연결합니다. myPickerView를 PickerView와 myTableView를 TableView와 myView를 View로 연결합니다.
 

이제 작업이 완료되었습니다. 빌드를 하고 테스트를 하여 봅니다. 아래는 전체 프로젝트를 압축한 파일입니다.


AND

사용자 삽입 이미지
맥과는 달리 아이폰에서는 아이콘 파일(*.icns)을 사용하지 않고, 57X57 사이즈의 PNG 파일 포맷을 사용하는 것 같습니다.





사용자 삽입 이미지
재미있는 것은 좌측이 원본 아이콘 이미지이고 우측이 아이폰의 홈스크린에서 보여지는 모습입니다. 시스템에서 자동으로 아이콘의 모서리를 둥글게 보여주고 명함 효과를 주기 때문에 이미지에서 별도의 작업이 필요 없습니다.

사용자 삽입 이미지
만약 이미지에 배경이 없고 투명색으로 처리되었을 경우에는 좌측과 같이 검정색 배경의 아이폰 아이콘의 형식으로 출력이 됩니다.




* 아이콘 파일 등록
아이콘 파일은 메뉴의 'Project/Add to Project...'나 Groups & Files에서 해당 디렉토리를 우클릭 한 후에 아래와 '같이 Add/Exsiting Files...'를 클릭하여 해당 파일을 선택하시면 됩니다.

사용자 삽입 이미지


* 아이콘 파일 지정
사용자가 지정하지 않으면 실행파일 번들내의 Icon.png를 기본 아이콘 파일로 인식합니다. 별도로 아이콘 파일을 지정할 경우에는 Info.plist 또는 아래와 같이 타겟의 Info Properties의 'Icon File:' 항목에서 png 확장자를 제외한 파일명을 입력합니다. 

사용자 삽입 이미지
AND

이전 장에 이어서 이제 인터페이스빌더에서 작업을 하고 어플리케이션을 완성해 보겠습니다. 속성 변경 및 연결에 관한 자세한 설명을 생략하였습니다. 혹시 이부분에 이해가 가지 않으시면 이전 포스팅을 참고하시기 바랍니다.

3. MainView.xib
1) 이미지 추가
어플리케이션에서 사용하는 배경과 각종 이미지들을 프로젝트에 추가합니다. 사용된 이미지는 아래의 압축파일을 다운로드하여 사용하시거나 직접 그려서 사용하셔도 됩니다.


Xcode의 Groups & Files내의 Resources 디렉토리를 우클릭 한후에 Add/Existing Files...를 클릭합니다.  파일 선택창에서 배경과 아이콘들을 선택한 다음 Add 버튼을 클릭합니다. 그리고 MainView.xib 항목을 더블클릭 하여 인터페이스빌더를 엽니다. 라이브러리의 Media 탭을 클릭하면 등록된 이미지를 확인할 수 있습니다.


2) 레이아웃
라이브러리 윈도우에서 각각의 오브젝트들을 드래그해서 아래와 같이 Main View에 배치합니다. 이미지도 위의 라이브러리 윈도우에서 드래그해서 가져옵니다. 주의하실 점은 눈에 보이지 않더라도 하단 툴바의 중앙에 Bar Button Item이 있습니다.
 


3) 속성 변경 및 연결
이제 각 항목의 속성을 변경하고 아울렛을 연결하는 작업을 해보겠습니다. 각 오브젝트와의 연결은 MainView.xib의 File's Owner (MainViewController)를 우클릭하여 설정합니다. 

* MainView
배경색을 원하시는 색상으로 변경합니다.


File's Owner의 mainView 아울렛에 연결합니다.


* Navigation Bar
상단바의 타이틀을 더블클릭하여 좌측과 같이 변경합니다.


* View
월별로 바이오리듬 그래프를 보여주는 View 입니다.
배경색을 검정으로 변경합니다. 실행시에는 배경이미지로 대체되기 때문에 큰 의미는 없습니다(생략가능).

Class 항목을 이전 장에서 만들어 두었던 GraphView를 선택합니다.

File's Owner의 graphView 아울렛에 연결합니다.


* TextField
일별로 바이오리듬 보여주는 필드입니다. 3개 모두 아래와 같이 설정합니다.
정렬을 중앙으로 설정합니다.

사용자의 입력에 반응하지 않도록 선택을 해제합니다.

위에서 부터 차례로 File's Owner의 value_1, value_2, value_3 아울렛과 연결합니다.


* Bar Button Item
버튼들을 더블클릭하여 좌측과 같이 입력상태가 되면 각각 "<<", "<", ">", ">>"을 입력합니다.

각 버튼을 구별할 수 있도록 속성창 하단에 위치한 Tag를 설정합니다. "<<"는 1, "<"는 2, ">"는 3, ">>"는 4를 각각 입력합니다.

네 버튼 모두 File's Owner의 NavigationButtonClicked: 액션에 연결합니다.


* 날짜 표시 Bar Button Item
현재 선택된 날짜를 표시하는 툴바의 중앙에 위치한 버튼 아이템입니다. Plain 스타일에 타이틀이 없기 때문에 위의 이미지에서 툴바와 구별되지 않았습니다.
 
스타일을 Plain으로 설정합니다. 버튼 스타일로 출력되지 않습니다.

사용자의 입력을 받지 않기 위해 선택을 해제합니다.

File's Owner의 currentDate 아울렛에 연결합니다.


File's Owner의 연결상태는 아래와 같습니다.



4. FlipsideView.xib
1) 레이아웃
라이브러리 윈도우에서 Label과 Date Picker를 드래그해서 아래와 같이 Flipside View에 배치합니다.


2) 속성 변경 및 연결
* Label
텍스트를 "생일을 선택해 주세요." 로 변경합니다.

텍스트 색상을 흰색으로 변경합니다.



* Date Picker
Date Picker의 Mode를 Date로 설정합니다.


유효날짜를 설정합니다.


File's Owner의 datePicker 아울렛에 연결합니다.


Files's Owner의 연결상태는 아래와 같습니다.


5. MainWindow.xib
MainWindow.xib에서 Root View Controller를 더블클릭합니다. 해당 창의 우측 하단을 보시면 좌측과 같은 버튼이 있습니다. 기본 위치가 툴바의 위치와 맞지 않기 때문에 위치를 우측과 아래로 더 이동합니다. 


6. 테스트
이제 모든 작업을 완료하였습니다. 모두 저장하고 빌드한 후에 테스트를 해봅니다. 아래와 같이 정상적으로 바이오리듬이 출력되고 버튼이 동작하는지 확인합니다.



이상으로 간단한 바이오리듬 어플리케이션을 만들어 보았습니다. 혹시 이상한 점이 있으시면 아래의 소스파일을 다운로드 받아서 비교해 보시기 바랍니다.

AND

이번에는 저번 바이오리듬 보다 조금 기능을 추가하여 만들어 보겠습니다. 상단에는 한달 단위로 그래프를 보여주고, 하단에는 해당일의 바이오리듬 정보를 보여 줍니다. 그리고 생일설정을 저장할 수 있도록 하겠습니다. 하단 버튼들의 기능은 아래와 같습니다.

  • << : 한달 전으로 이동
  • < : 하루 전으로 이동
  • > : 하루 후로 이동
  • >> : 한달 후로 이동
  • i : 생일 설정 

한가지 오류가 있습니다. 생일설정 시에 기존의 저장된 날짜가 DatePicker에 설정되지 않습니다. 값은 정확한데 보여지는 부분은 minimumDate로 초기에 선택되어져 있는 것 같습니다. 검색을 해봐도 같은 증상을 겪은 경우는 보았는데, 원인과 해결책은 찾지 못했습니다.


완성된 모습은 아래와 같습니다.

사용자 삽입 이미지


1. 프로젝트 생성
사용자 삽입 이미지
Xcode의 메뉴에서 File/New Project 클릭합니다. iPhone OS의 applicaion에서 좌측과 같은 Unility Application을 선택하고 Choose 버튼을 클릭합니다. 프로젝트 이름에 'iBiorhythm'을 입력한 후에 저장합니다.

Unility Application은 컨텐츠를 보여주는 main view와 설정등을 할 수 있는 flipside view를 제공하며, 두 뷰의 변환시에 아래와 같은 에니메이션 효과를 제공합니다.

사용자 삽입 이미지

사용자 삽입 이미지
Xcode의 Groups & Files를 보시면 좌측과 같이 그룹이 생성되어 있습니다. MainView, FlipsideView 각각에는 view와 view를 관리하는 controller 소스들이 있습니다.


2. 소스코드 
1) Global.h
사용자 삽입 이미지
여러 소스에서 사용할 상수나 변수등을 위해 헤더 파일을 하나 만듭니다. File/New File...을 클릭합니다. Mac OS X에서 C and C++ 항목을 클릭하고 좌측과 같은 Header File이란 아이콘을 선택한 후에 파일명을 Global.h로 입력하고 저장합니다. 생성된 파일에 아래와 같은 내용을 추가 합니다.

#define    MAX_DATATYPE            3

#define US_BIRTHYEAR_KEY        @"birth_year"
#define US_BIRTHMONTH_KEY     @"birth_month"
#define US_BIRTHDAY_KEY          @"birth_day"

#define NM_BIRTHDAYCHANGED   @"NTBirthdayChanged"

#define NAV_PREVMONTH            1
#define NAV_PREVDAY                 2
#define NAV_NEXTDAY                 3
#define NAV_NEXTMONTH            4

#define ONEDAY_SECOND            (24 * 60 * 60)

2) GraphView
그래프를 출력하기 위한 View를 생성합니다. 메뉴에서 File/New File...을 클릭 후에 iPhone OS/Cocoa Touch Classes 항목에서 UIView subclass를 선택한 후에 Next 버튼을 클릭합니다. 파일명에 GraphView.m을 입력한 후에 저장합니다.

* GraphView.h
#import <UIKit/UIKit.h>

@interface GraphView : UIView {
    UIImage                *backgroundImage;
   
    NSDateComponents    *currentDate;
    NSDateComponents    *birthDate;
   
    float biorhythmData[31][MAX_DATATYPE];
}

@property (nonatomic, retain) NSDateComponents *currentDate;
@property (nonatomic, retain) NSDateComponents *birthDate;

- (void)setBirthDate:(int)year atMonth:(int)month atDay:(int)day;
- (void)setCurrentDate:(int)year atMonth:(int)month atDay:(int)day;
- (void)changeDate:(int)pos;

- (NSDateComponents *)currentDate;

- (void)resetBiorhythmData;
- (float *)getBiorhythmDayData:(int)day;

@end

* GraphView.m
#import "Global.h"
#import "GraphView.h"

@implementation GraphView

@synthesize currentDate;
@synthesize birthDate;

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
    }
    return self;
}

- (void)awakeFromNib {
    currentDate = [[NSDateComponents alloc] init];
   
    /* 배경 이미지로 사용할 bio.png 로드 */
    backgroundImage = [[UIImage alloc] initWithContentsOfFile:
                       [[NSBundle mainBundle] pathForResource:@"bio" ofType:@"png"]];
}

- (void)drawRect:(CGRect)rect {
#define START_X            17    // 그래프 시작 X 위치
#define START_Y            22  // 그래프 시작 Y 위치
#define DAY_WIDTH        10  // 일별 간격
#define BOTTOM_HEIGHT    4   // 하단 공백 높이

    /* 데이터별 색상 테이블 */
    static const CGFloat color[MAX_DATATYPE][5] = {
        { 1.0, 0.0, 0.0, 1.0 }, //red
        { 0.0, 1.0, 0.0, 1.0 }, //green
        { 0.0, 0.0, 1.0, 1.0 }  //blue
    };
   
    CGSize viewSize = rect.size;
    CGContextRef context = UIGraphicsGetCurrentContext();

    /* 배경 이미지 출력 */
    CGPoint point;
    point.x = point.y = 0;
    [backgroundImage drawAtPoint:point];

    /* 오늘 날짜를 가르키는 위치는 노란 선으로 출력 */
    CGContextBeginPath(context);
    CGContextSetLineWidth(context, 1.0);
    CGContextSetRGBStrokeColor(context, 1.0, 0.8, 0.0, 1.0);
    CGContextMoveToPoint(context, START_X + (DAY_WIDTH * ([currentDate day] - 1)), START_Y);
    CGContextAddLineToPoint(context, START_X + (DAY_WIDTH * ([currentDate day] - 1)), viewSize.height - BOTTOM_HEIGHT);
    CGContextStrokePath(context);
   
    /* 바이오리듬 출력 */
    int i, j;
    int lineX = START_X;
   
    int vcenter = START_Y + (viewSize.height - START_Y - BOTTOM_HEIGHT)/2; // 그래프의 Y 중간 좌표
   
    /* 바이오리듬 출력 */
    for (i = 1; i < 31; i++) {
        for(j = 0; j < MAX_DATATYPE; j++) {
            CGContextBeginPath(context);
            CGContextSetLineWidth(context, 2.0);
           
            CGContextSetStrokeColor(context, color[j]);
           
            CGContextMoveToPoint(context, lineX, vcenter - biorhythmData[i-1][j]);
            CGContextAddLineToPoint(context, lineX + DAY_WIDTH, vcenter - biorhythmData[i][j]);
            CGContextStrokePath(context);
        }
        lineX += DAY_WIDTH;
    }
}

- (void)dealloc {
    [backgroundImage release];
    [currentDate release];
    [super dealloc];
}

- (void)resetBiorhythmData {
    static const double s_values[MAX_DATATYPE] = {
        23.0, 33.0, 28.0 // 신체, 지성, 감성
    };
   
    /* 현재 설정된 날짜의 1일 부터 생일 사이의 날수를 구한다. */
    NSDateComponents *firstDate = [[NSDateComponents alloc] init];
    [firstDate setYear: [currentDate year]];
    [firstDate setMonth: [currentDate month]];
    [firstDate setDay:1];
   
    NSDate *tempDay = [[NSCalendar currentCalendar] dateFromComponents:firstDate];
    NSDate *birthDay = [[NSCalendar currentCalendar] dateFromComponents:birthDate];
   
    NSTimeInterval ti = [tempDay timeIntervalSinceDate:birthDay];
    [firstDate release];

    /* 바이오리듬 데이터를 구한다 */
    double days = ceil(fabs(ti) / ONEDAY_SECOND);
   
    for (int i = 0; i < 31; i++) {
        for(int j = 0; j < MAX_DATATYPE; j++) {
            biorhythmData[i][j] = sin((days/s_values[j]) * 2 * 3.14195) * 100;
        }
        days += 1.0;
    }
   
    /* 변경된 데이터로 다시 그림 */
    [self setNeedsDisplay];
}

/* 현재 날짜 변경 */
- (void)setCurrentDate:(int)year atMonth:(int)month atDay:(int)day {
    [currentDate setYear:year];
    [currentDate setMonth:month];
    [currentDate setDay:day];
}

/* 생일 변경 */
- (void)setBirthDate:(int)year atMonth:(int)month atDay:(int)day {
    [birthDate setYear:year];
    [birthDate setMonth:month];
    [birthDate setDay:day];
   
    NSLog(@"SET BIRTHDAY: %d, %d, %d", year, month, day);
}

/* 사용자가 버튼을 클릭했을 때, 날짜를 변경한다 */
- (void)changeDate:(int)pos {
    NSRange monthRange;
   
    NSTimeInterval newValue = 0;
    NSDate *tempDate = [[NSCalendar currentCalendar] dateFromComponents:currentDate];
   
    if (pos == NAV_PREVMONTH) {
        /* 이전 달로 이동을 위해 이전 달의 날수를 구한다 */
        NSDateComponents *prevMonthDate = [[NSDateComponents alloc] init];
        [prevMonthDate setYear: [currentDate year]];
        [prevMonthDate setMonth: [currentDate month]-1];
        [prevMonthDate setDay:1];
       
        monthRange = [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit
                    inUnit:NSMonthCalendarUnit
                    forDate:[[NSCalendar currentCalendar] dateFromComponents:prevMonthDate]];
        [prevMonthDate release];
       
        /* 한달 전으로 이동 */
        newValue = ONEDAY_SECOND * monthRange.length;
        newValue = -newValue;
    }
    else if (pos == NAV_PREVDAY) {
        /* 하루 전으로 이동 */
        newValue = -ONEDAY_SECOND;
    }   
    else if (pos == NAV_NEXTDAY) {
        /* 하루 후로 이동 */
        newValue = ONEDAY_SECOND;
    }   
    else if (pos == NAV_NEXTMONTH) {
        /* 다음 달로 이동을 위해 현재 달의 날수를 구한다 */
        monthRange = [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit
                                                        inUnit:NSMonthCalendarUnit
                                                       forDate:[[NSCalendar currentCalendar] dateFromComponents:currentDate]];
        /* 한달 후로 이동 */
        newValue = ONEDAY_SECOND * monthRange.length;
    }

    /* 날짜를 변경하고 변경된 날짜를 구한다. */
    NSDateComponents *newDate = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit)
                                                            fromDate:[tempDate addTimeInterval:newValue]];
   
    /* 현재 날짜를 변경 */
    [currentDate setYear: [newDate year]];
    [currentDate setMonth: [newDate month]];
    [currentDate setDay: [newDate day]];
   
    /* 바이오리듬을 다시 계산하고 출력한다. */
    [self resetBiorhythmData];
}

- (float *)getBiorhythmDayData:(int)day {
    return biorhythmData[day-1];
}

- (NSDateComponents *)currentDate {
    return currentDate;
}
@end


3) MainView
* MainView.h
#import <UIKit/UIKit.h>

@interface MainView : UIView {
    UIImage     *barFrame[MAX_DATATYPE];
    double        bioData[MAX_DATATYPE];
}

-(void)setData:(int)value1 secondValue:(int)value2 thirdValue:(int)value2;

@end

* MainView.m
#import "Global.h"
#import "MainView.h"

@implementation MainView

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
    }
    return self;
}

- (void)awakeFromNib {
    /* 바의 배경을 위한 이미지를 로드한다. */
    for (int i = 1; i <= MAX_DATATYPE; i++) {
        barFrame[i - 1] = [[UIImage alloc] initWithContentsOfFile:
         [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"frame%d", i] ofType:@"png"]];
    }
}

- (void)drawRect:(CGRect)rect {
#define CENTER_X        210  // 바의 중간(0) 위치
#define START_Y            300  // 바가 출력될 Y 위치
#define START_X            120  // 바가 출력될 X 위치
#define BAR_HEIGHT        14.0 // 바의 높이   
#define    BAR_SPACE        40   // 각 바간 간격
   
    // Drawing code
    CGContextRef context = UIGraphicsGetCurrentContext();
   
    int y = START_Y + 15;
    CGPoint point;
   
    point.y = START_Y;
    point.x = START_X;
   
    for (int i = 0; i < MAX_DATATYPE; i++) {
        /* 배경 이미지 출력 */
        [barFrame[i] drawAtPoint:point];
       
        CGContextBeginPath(context);
        CGContextSetLineWidth(context, BAR_HEIGHT);
       
        /* 바이오리듬 값이 0보다 작으면 붉은 색으로 크면 파란색으로 표시 */
        if (bioData[i] > 0)
            CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
        else
            CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
       
        /* 바를 그린다 */
        CGContextMoveToPoint(context, CENTER_X, y);
        CGContextAddLineToPoint(context, CENTER_X + (bioData[i] * 80/100) , y);
       
        CGContextStrokePath(context);

        /* Y 좌표 변경 */
        point.y += BAR_SPACE;
        y += BAR_SPACE;
    }
}

-(void)setData:(int)value1 secondValue:(int)value2 thirdValue:(int)value3 {
    bioData[0] = value1;
    bioData[1] = value2;
    bioData[2] = value3;
}

- (void)dealloc {
    for (int i = 0; i < MAX_DATATYPE; i++) {
        [barFrame[i] release];
    }
   
    [super dealloc];
}

@end


4) MainViewController
* MainViewController.h
#import <UIKit/UIKit.h>

@class GraphView;
@class MainView;

@interface MainViewController : UIViewController {
    IBOutlet MainView        *mainView;   
    IBOutlet GraphView      *graphView;
    IBOutlet UITextField    *value_1;
    IBOutlet UITextField    *value_2;
    IBOutlet UITextField    *value_3;
   
    IBOutlet UIBarButtonItem *currentDate;
}

- (void)updateInfoUI;
- (void)birthdayChanged:(NSNotification *)note;

- (IBAction)navigationButtonClicked:(id)sender;

@end

* MainViewController.m
#import "Global.h"
#import "MainViewController.h"
#import "MainView.h"
#import "GraphView.h"

@implementation MainViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad {
    /* 오늘 날짜를 구한다. */
    NSDateComponents *today = [[NSCalendar currentCalendar]
                       components:(NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit)
                       fromDate:[NSDate date]];
   
    /* 현재 날짜 설정 */
    [graphView setCurrentDate:[today year]
                      atMonth:[today month]
                        atDay:[today day]];

    /* 생년월일 설정 */
    [graphView setBirthDate:[[NSUserDefaults standardUserDefaults] integerForKey:US_BIRTHYEAR_KEY]
                      atMonth:[[NSUserDefaults standardUserDefaults] integerForKey:US_BIRTHMONTH_KEY]
                        atDay:[[NSUserDefaults standardUserDefaults] integerForKey:US_BIRTHDAY_KEY]];

    /* 그래프를 그리고 UI를 설정한다. */
    [graphView resetBiorhythmData];
    [self updateInfoUI];
   
    /* 환경설정에서 생일이 변경되었을 때를 위해 옵저버로 등록 */
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(birthdayChanged:)
                                                 name:NM_BIRTHDAYCHANGED
                                               object:nil];
}

- (void)updateInfoUI {
    /* 하단 타이틀에 현재 날짜를 출력 */
    NSDateComponents *date = [graphView currentDate];
    [currentDate setTitle:[NSString stringWithFormat:@"%d.%02d.%02d",
                           [date year], [date month], [date day]]];
   
    float *bioValue = [graphView getBiorhythmDayData:[date day]];
    int value1 = (int)ceil(bioValue[0]);
    int value2 = (int)ceil(bioValue[1]);
    int value3 = (int)ceil(bioValue[2]);
   
    /* 각 바이오리듬 값 출력 */
    [value_1 setText:[NSString stringWithFormat:@"%d", value1]];
    [value_2 setText:[NSString stringWithFormat:@"%d", value2]];
    [value_3 setText:[NSString stringWithFormat:@"%d", value3]];
   
    /* mainView에 현재 설정된 값을 알려주고, 바가 다시 그려지도록 한다. */
    [mainView setData:value1 secondValue:value2 thirdValue:value3];
    [mainView setNeedsDisplay];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [super dealloc];
}

- (void)birthdayChanged:(NSNotification *)note
{
    /* 생일이 변경되었을 때 다시 설정하고 그려지도록 한다. */
    [graphView setBirthDate:[[NSUserDefaults standardUserDefaults] integerForKey:US_BIRTHYEAR_KEY]
                      atMonth:[[NSUserDefaults standardUserDefaults] integerForKey:US_BIRTHMONTH_KEY]
                        atDay:[[NSUserDefaults standardUserDefaults] integerForKey:US_BIRTHDAY_KEY]];
   
    [graphView resetBiorhythmData];
    [self updateInfoUI];
}       

- (IBAction) navigationButtonClicked:(id)sender {
    /* 사용자가 날짜 이동 버튼을 클릭하였을 경우 처리 */
    [graphView changeDate:[sender tag]];
    [self updateInfoUI];
}

@end


5) FlipsideViewController
* FlipsideViewController.h
#import <UIKit/UIKit.h>

@interface FlipsideViewController : UIViewController {
    IBOutlet UIDatePicker    *datePicker;
}

@property (nonatomic, retain) UIDatePicker *datePicker;

@end

* FlipsideViewController.m
#import "FlipsideViewController.h"

@implementation FlipsideViewController

@synthesize datePicker;

- (void)viewDidLoad {
    self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];       
 }

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [super dealloc];
}

@end


5) RootViewController
* RootViewController.m
#import "Global.h"
#import "RootViewController.h"
#import "MainViewController.h"
#import "FlipsideViewController.h"

@implementation RootViewController

@synthesize infoButton;
@synthesize flipsideNavigationBar;
@synthesize mainViewController;
@synthesize flipsideViewController;

- (void)viewDidLoad {
   
    MainViewController *viewController = [[MainViewController alloc] initWithNibName:@"MainView" bundle:nil];
    self.mainViewController = viewController;
    [viewController release];
   
    [self.view insertSubview:mainViewController.view belowSubview:infoButton];
}

- (void)loadFlipsideViewController {
   
    FlipsideViewController *viewController = [[FlipsideViewController alloc] initWithNibName:@"FlipsideView" bundle:nil];
    self.flipsideViewController = viewController;
    [viewController release];
   
    // Set up the navigation bar
   
    UINavigationBar *aNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
    aNavigationBar.barStyle = UIBarStyleBlackOpaque;
    self.flipsideNavigationBar = aNavigationBar;
    [aNavigationBar release];
   
    UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(toggleView)];
    UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"iBiorhythm"];
    navigationItem.rightBarButtonItem = buttonItem;
    [flipsideNavigationBar pushNavigationItem:navigationItem animated:NO];
    [navigationItem release];
    [buttonItem release];
}

- (IBAction)toggleView {   
    if (flipsideViewController == nil) {
        [self loadFlipsideViewController];
    }
   
    UIView *mainView = mainViewController.view;
    UIView *flipsideView = flipsideViewController.view;
   
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:1];
    [UIView setAnimationTransition:([mainView superview] ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES];
   
    if ([mainView superview] != nil) {
        [flipsideViewController viewWillAppear:YES];
        [mainViewController viewWillDisappear:YES];
        [mainView removeFromSuperview];
        [infoButton removeFromSuperview];
        [self.view addSubview:flipsideView];
        [self.view insertSubview:flipsideNavigationBar aboveSubview:flipsideView];
        [mainViewController viewDidDisappear:YES];
        [flipsideViewController viewDidAppear:YES];
       
        /* DataPicker 날짜를 저장된 날짜로 설정 (현재 동작하지 않음) */
        NSDateComponents *curDate = [[NSDateComponents alloc] init];
       
        int year = [[NSUserDefaults standardUserDefaults] integerForKey:@"birth_year"];
        int month = [[NSUserDefaults standardUserDefaults] integerForKey:@"birth_year"];
        int day = [[NSUserDefaults standardUserDefaults] integerForKey:@"birth_year"];

        [curDate setYear: year];
        [curDate setMonth: month];
        [curDate setDay: day];
   
        NSDate *tempDate = [[NSCalendar currentCalendar] dateFromComponents:curDate];
        [[flipsideViewController datePicker] setDate:tempDate animated:YES];
           
        [curDate release];
    } else {
        [mainViewController viewWillAppear:YES];
        [flipsideViewController viewWillDisappear:YES];
        [flipsideView removeFromSuperview];
        [flipsideNavigationBar removeFromSuperview];
        [self.view addSubview:mainView];
        [self.view insertSubview:infoButton aboveSubview:mainViewController.view];
        [flipsideViewController viewDidDisappear:YES];
        [mainViewController viewDidAppear:YES];
       
        /* 사용자가 설정한 날짜(생일)를 저장 한다 */
        NSDateComponents *newDate = [[NSCalendar currentCalendar]
              components:(NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit)
              fromDate:[[flipsideViewController datePicker] date]];
       
        [[NSUserDefaults standardUserDefaults] setInteger:[newDate year] forKey:US_BIRTHYEAR_KEY];
        [[NSUserDefaults standardUserDefaults] setInteger:[newDate month] forKey:US_BIRTHMONTH_KEY];
        [[NSUserDefaults standardUserDefaults] setInteger:[newDate day] forKey:US_BIRTHDAY_KEY];
       
        /* 바이오리듬이 변경되도록 메시지를 보낸다. */
        [[NSNotificationCenter defaultCenter]
                   postNotificationName:NM_BIRTHDAYCHANGED
                   object:self];
    }
    [UIView commitAnimations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [infoButton release];
    [flipsideNavigationBar release];
    [mainViewController release];
    [flipsideViewController release];
    [super dealloc];
}

@end

다음 포스팅에선 인터페이스 빌더에서 작업을 하고 어플리케이션이 동작하도록 완성시켜 보겠습니다. 동작은 하는데 제대로 만든 것인지는 모르겠습니다.
AND

간단하게 아이폰용 바이오리듬 어플리케이션을 만들어 보겠습니다. 저도 처음 아이폰 SDK를 사용하여 만들어 보았기 때문에, 과정을 가능한 상세하게 설명할려고 합니다. 바이오리듬은 이전에 데쉬보드 위젯에서의 방식을 사용 하였습니다. 역시나 제대로 된 방법인지는 잘 모르겠습니다.




1. 프로젝트 생성
Xcode를 실행하여 파일 메뉴에서 New Project를 클릭합니다. iPhone OS의 Application 항목에서 가장 마지막에 있는 'Window-Based Application'을 선택한 후 Choose... 버튼을 클릭합니다.

프로젝트명을 'iBio'로 입력하고 Save 버튼을 클릭합니다.


2. iBioView
1) 클래스생성
바이오리듬을 막대그래프 형식으로 보여줄 View 클래스를 만들어 보겠습니다. 메뉴에서 파일/New File...(또는 단축키: ⌘+N)을 클릭합니다. IPhone OS/Cocoa Touch Classes를 클릭하고 아래와 같이 UIView subclass를 선택한 다음 Next 버튼을 클릭합니다.


다음 창에서 아래와 같이 File Name에 iBioView.m을 입력한 후에 Finish 버튼을 클릭합니다.

2) iBioView.h
iBioView.h 파일에 아래와 같이 내용을 추가 합니다.
#import <UIKit/UIKit.h>

#define MAX_DATATYPE        3

@interface iBioView : UIView {
   double bioData[MAX_DATATYPE];
}

- (void)showBioData: (double)days;
- (int)getBioDataAt: (int)index;

@end

MAX_DATATYPE은 바이오리듬 항목을 의미하며, 신체/지성/감성 등 3개의 항목을 가지고 있습니다. bioData에는 각 항목별로 오늘의 바이오리듬 값이 저장됩니다.

3) iBioView.m
iBioView.m 파일에 아래와 같이 내용을 추가 합니다. 설명은 소스내의 간단한 주석으로 대신하겠습니다.
#import "iBioView.h"

@implementation iBioView

- (id)initWithFrame:(CGRect)frame {
   if (self = [super initWithFrame:frame]) {
          /* 바이오리듬 값을 0으로 초기화 */

           for (int i = 0; i < MAX_DATATYPE; i++) {
               bioData[i] = 0.0;

       }
   }
   return self;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
   
   /* 세개의 바이오리듬 값 막대 출력 */
    for (int i = 0; i < MAX_DATATYPE; i++) {
        CGContextBeginPath(context);
        CGContextSetLineWidth(context, 15.0);
       
        int y = 34 + (i * 40);
      
       /* 바이오리듬 값이 0보다 작으면 붉은 색으로 크면 파란색으로 표시 */
        if (bioData[i] > 0)
            CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
        else
            CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
      
       /* 선을 그린다 */
        CGContextMoveToPoint(context, 200, y);
        CGContextAddLineToPoint(context, 200 + (bioData[i] * 80/100) , y);
       
        CGContextStrokePath(context);
    }
}

- (void)dealloc {
   [super dealloc];
}

- (void)showBioData: (double)days {
    static const double s_values[MAX_DATATYPE] = {
        23.0, 28.0, 33.0
    };
   
   /* 각 바이오리듬 설정 */
    for (int i = 0; i < MAX_DATATYPE; i++) {
        bioData[i] = sin((days/s_values[i]) * 2 * 3.14195) * 100;
    }
   
   /* 변경된 값으로 view가 다시 그려지도록 한다 */
    [self setNeedsDisplay];
   
}

- (int)getBioDataAt: (int)index {
    return (int)bioData[index];
}

4) 프레임 워크 추가
CGContextBeginPath와 같이 코어그래픽스 프레임워크의 모듈을 사용하기 위해 해당 프레임워크를 추가해야 합니다. 아래와 같이 Xcode 좌측의 Frameworks에서 마우스를 우클릭하여 Add/Existing Frameworks... 메뉴를 선택합니다.


선택창이 열리면 /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks  디렉토리에서 /CoreGraphics.framework를 선택합니다.

* 확인
완료되면 좌측과 같이 CoreGraphics.framework가 추가되어 있는 것을 확인할 수 있습니다.




3. iBioAppDelegate 변경
1) iBioAppDelegate.h
iBioAppDelegate.h 파일에 아래의 내용을 추가합니다. 추가된 항목은 인터페이스 빌더에서 각각의 오브젝트와 액션으로 연결됩니다.

#import <UIKit/UIKit.h>

@class iBioViewController;
@class iBioView;

@interface iBioAppDelegate : NSObject <UIApplicationDelegate> {
   IBOutlet UIWindow *window;

    IBOutlet iBioView *view;
    IBOutlet UIDatePicker *datePicker;
   
    IBOutlet UITextField *text_1;
    IBOutlet UITextField *text_2;
    IBOutlet UITextField *text_3;
}

@property (nonatomic, retain) UIWindow *window;

- (IBAction) viewButtonClicked:(id)sender;

@end


2) iBioAppDelegate.m
iBioAppDelegate.m 파일에 아래의 내용을 추가합니다.
#import "iBioAppDelegate.h"
#import "iBioView.h"

@implementation iBioAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {   
   
    // Override point for customization after app launch   
    [window makeKeyAndVisible];
}


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

/* 사용자가 보기 버튼을 클릭시에 메시지를 받는 메소드 */
- (IBAction)viewButtonClicked:(id)sender {
    /* 사용자가 DatePicker에서 입력한 생일과 현재 날짜로 부터 차이를 구함 */
    NSTimeInterval ti = [[datePicker date] timeIntervalSinceNow];
    double days = ceil(fabs(ti) / (24 * 60 * 60));
   
   /* view에서 days를 기준으로 바이오리듬을 구하고 출력되도록 한다 */
    [view showBioData: days];
   
   /* 각 텍스트필드에 새로 계산된 바이오리듬 값으로 설정 */ 
    [text_1 setText: [NSString stringWithFormat: @"%d", [view getBioDataAt:0]]];
    [text_2 setText: [NSString stringWithFormat: @"%d", [view getBioDataAt:1]]];
    [text_3 setText: [NSString stringWithFormat: @"%d", [view getBioDataAt:2]]];
}

@end

* 인터페이스 빌더 오픈
변경된 모든 파일들을 저장하고 좌측과 같이 MainWindow.xib를 더블클릭하여 인터페이스 빌더를 실행합니다.



4. 사용자 인터페이스 생성
1) DatePicker
* Library 윈도우
사용자가 생일을 입력할 수 있도록 DatePicker를 설정합니다.


위와 같이 라이브러리 윈도우의 Cocoa Touch Plugin/Data Views에서 Date Picker를 클릭한 후에 드래그 하여 윈도우에 가져다 놓습니다. 아래가 윈도우에 위치한 모습니다.


* 속성 변경
위의 윈도우에서 DatePicker가 선택된 상태에서 속성창에서 좌측과 같이 설정합니다.

속성창이 안보이면 선택된 상태에서  단축키(⌘+1)를 이용합니다.










2) ToolBar
다시 라이브러리 윈도우의 Windows, Views & Bars 항목에서 좌측과 같은 Toolbar 아이콘을 드래그 하여 윈도우의 하단에 아래와 같이 배치합니다.



* 타이틀 변경
툴바의 Item 버튼을 더플클릭하면 좌측과 같이 입력 가능한 상태로 변경됩니다. '보기'라고 입력합니다.



3) 안내문구
다시 라이브러리 윈도우의 Inputs & Values 항목에서 Label을 윈도우의 DatePicker 위로 드래그 해서 놓습니다.


* 속성 변경
좌측과 같이 속성창에서 Text에  "생일을 선택한 후 보기를 클릭하세요" 라고 입력합니다.

하단의 Layout에서 정렬을 중앙 정렬로 놓습니다.






* 확인
현재까지 윈도우의 모습은 아래와 같습니다.
사용자 삽입 이미지


4) View
다시 라이브러리 윈도우의 Windows, Views & Bars 항목에서 좌측과 같은 View 아이콘을 드래그 하여 크기를 조절하여 윈도우의 상단에 아래와 같이 배치합니다.
* 배경 색상 변경
속성창의 Background의 색상선택 버튼을 클릭하여 좌측과 같이 검정색으로 설정합니다.








* 클래스 변경
Class에 Xcode에서 만들어 두었던 iBioView를 선택합니다.





5) 제목, 텍스트 필드
* 제목 라벨
라이브러리 윈도우에서 Label을 세번 드래그 해서 좌측과 같이 배치합니다. 

라벨을 더블클릭하여 입력모드가 되면 각각 '신체:', '감성:', '지성:'으로 입력합니다.










* 텍스트 필드
라이브러리 윈도우에서 좌측과 같은 TextFiled도 똑같이 세번 드래그 해서 이전에 배치한 라벨의 옆에 아래와 같이 배치합니다.



* 속성 변경
⌘를 누른 상태에서 마우스로 클릭하여 좌측과 같이 세개의 텍스트 필드를 선택합니다. ⌘+1로 속성창을 엽니다.







사용자로 부터 입력에 반응하지 않기 위해 'User Interaction Eanbled'를 체크를 해제합니다.



5. 연결
MainWindow.xib 윈도우에서 I Bio App Delegate를 마우스로 우클릭합니다. 아래와 같이 iBioAppDelegate.h에서 생성해 놓은 Outlet들과 'viewButtonCliecked' Action이 있습니다. 이제 실제 오브젝트들과 연결해 보겠습니다.


1) Date Picker 연결
dataPicker 우측의 원 모양의 아이콘을 클릭하면 좌측과 같이 +로 모양이 변경됩니다. 이 상태에서 마우스 버튼을 클릭한 채로 아래와 같이 윈도우의 DatePicker에 드래그해서 놓습니다.


* 확인
완료되면 좌측과 같이 연결된 오브젝트가 표시되며, 우측의 원 모양 아이콘이 on으로 표시되어 연결되어 있슴을 알려 줍니다. x 모양의 아이콘을 클릭하면 연결이 해제됩니다.

2) TextFiled 연결
위와 같은 방법으로 아래와 같이 TextField를 text_1, text_2, text3과 위에서 부터 순서대로 연결합니다.

3) View 연결
같은 방법으로 아래와 같이 view도 연결합니다.

4) viewButtonClicked 메소드 연결
사용자가 보기 버튼을 클릭하면 viewButtonClicked가 실행되도록 위와 같은 방법으로 viewButtonClicked를 툴바의 보기 버튼과 연결합니다.

5) 연결 확인

좌측과 같이 모든 항목들이 정확하게 연결이 되어 있는 것을 확인합니다.

이제 모두 완료되었습니다. 저장 후에 빌드를 한후 실행하여 동작을 확인해 봅니다.









이상 간단하게 첫 아이폰 어플리케이션을 만들어 보았습니다. DatePicker도 동작과 설정이 조금 이상한 것 같고, 처음 갑작스레 만들어 본 것이라 맞는 방법인지 의심스럽습니다. 혹시 잘못된 부분이 있으면 댓글로 알려 주시면 감사하겠습니다.

기본 UI들이 멋있어서 맞출려면 디자인이나 이미지에 신경을 써야 할 것 같습니다. 나중에 조금 더 알아본 후에 월별로 출력하도록 수정하고 몇가지 변경해 본 후에 다시 한번 올려 보겠습니다.
AND

iOS 2008. 5. 26. 14:05
사용자 삽입 이미지
어제 iPhone SDK를 다운로드 받고 설치를 해보았습니다. SDK는 애플의 다운로드 페이지에서 받으실 수 있습니다. 실행환경은 OS X 10.5.2 이상의 인텔맥에서만 사용할 수 있습니다. 최신 버젼은 2008년 5월 8일에 올라 왔으며 파일크기가 1GB가 조금 넘었습니다.

 Xcode 3과 레오퍼드의 개발툴들을 잘 몰라서 설치 후 무엇이 추가되고 변경되었는지는 잘 모르겠습니다. 다만 Xcode와 Dashcode에서 차이점을 발견할 수 있었습니다.

1. Xcode
그러고 보니 아직 Xcode 3에서 한번도 New Project를 실행해 본적이 없었습니다. 그렇지만 아래의 iPhone OS란 메뉴가 SDK 설치로 새로 생겼을 것으로 짐작은 됩니다.

사용자 삽입 이미지

이름을 보니까 아이폰 SDK에서 사용하는 Cocoa는 'Cocoa Touch'로 부르는 것 같습니다. 일단 'Cocoa Touch Application'을 선택하고 프로젝트를 만들어 보았습니다.

사용자 삽입 이미지
프로젝트 명을 'iPhoneSDK'로 하였더니 좌측과 같은 파일들이 기본적으로 생성되었습니다.
 
기존 Cocoa 프로젝트에서 볼 수 있었던 파일들도 보이고 iPhoneSDKAppDelegate 클래스와 MainWindow.xib와 몇개의 프레임워크등 새로 추가된 부분들도 보입니다.

NIB Files가 비어 있어 순간 인터페이스빌더를 지원하지 않는가 생각했는데 MainWindow.xib를 더블클릭하니 인터페이스 빌더를 볼 수 있었습니다. nib의 n이 NeXTSTEP을 의미하는 것으로 알고 있는데 xib의 x가 OS X를 의미하는지는 모르겠습니다. (나중에 OS X 어플리케이션으로 만들어 봐도 nib 파일 대신 xib가 생겼습니다. 이 부분은 Xcode 3에서 변경된 것 같습니다.)

사용자 삽입 이미지
   
우측에 생략된 전체 이름은 'I PhoneSDK App Delegate' 입니다. 

사용자 삽입 이미지
라이브러리 팔레트에서는 좌측과 같이 사용할 수 있는 오브젝트나 컨트롤들의 목록이 있습니다. 이전에 아이폰의 사진에서 보았던 버튼이나 UI들도 보입니다.

몇개 끌고 와서 윈도우에 배치를 해 보았습니다. 실제 구현은 아직 모르기 때문에 무조건 빌드를 하고 실행을 해 보았습니다.





사용자 삽입 이미지
특이하게 애플은 일반적으로 사용하는 에뮬레이터가 아니라 시뮬레이터라고 이름을 붙였습니다. 아이폰 시뮬레이터를 살펴 보는데 아직 베타 버젼이라 그런지 제가 잘 못 해서 그런지 실행된 어플리케이션의 입력란에 포커스가 갈 경우에는 시뮬레이터가 다운되었습니다. 


아래의  좌측은 인터페이스 빌더에서 작업한 모습이고 우측은 빌드 후에 아이폰 시뮬레이터에서 실행된 모습니다.

사용자 삽입 이미지

자동으로 생성된 코드들은 아래와 같습니다. main에 추가 된것은 UIApplicationMain에 대한 호출입니다. 실제 작업은 그 이름과 같이 AppDelegate 클래스에서 수행해야 하는 것 같습니다. 아마 UIKit이 아이폰의 핵심 프래임워크 같습니다.

main.m
#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;
}

iPhoneSDKAppDelegate.h
#import <UIKit/UIKit.h>

@interface iPhoneSDKAppDelegate : NSObject <UIApplicationDelegate> {
    IBOutlet UIWindow *window;
}

@property (nonatomic, retain) UIWindow *window;

@end

iPhoneSDKAppDelegate.m
#import "iPhoneSDKAppDelegate.h"

@implementation iPhoneSDKAppDelegate
@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {   
    // Override point for customization after app launch
}

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

@end


2. Dashcode
혹시나 하는 마음에 Dashcode도 실행해 보았습니다. 아래와 같이 New Project에서 Web Application이란 추가된 항목이 있었습니다.

사용자 삽입 이미지

이전 포스팅에서 한번 보았던 RSS를 선택하고 새 프로젝트를 만들었습니다. 이전과 동일하게 속성의 Feed URL에 올블로그 주소만 입력하고 실행해 보았습니다. 아래와 같이 시뮬레이터가 실행되었습니다.

사용자 삽입 이미지
아마 Dashcode로 만든 어플리케이션은 사파리내에서 실행되는 거 같습니다. 웹어플리케이션이니까 당연히 웹플랫폼에서 실행되겠지만 주소창이나 브라우져의 UI들은 안 보여 줄 것으로 생각했는데 아닌 것 같습니다.

아직 Xcode 3도 모르고 간만 본 상태에서 조금 이른 생각 같지만 iPhone에서의 개발은 기존의 다른 모바일 플랫폼 보다 쉬운 것 같습니다. 신경써야 될 폰이 아이폰 딱 하나인 것도 개발자 입장에선 편할 것 같고요. 개발자료들도 ADC에 충분한 것 같으니 많은 어플리케이션들이 나올 것 같습니다.
SDK와 툴들을 대충 둘러 보면서 느낀 첫번째 생각은 우리나라에 나오기만 하면 꼭 아이폰을 사야 겠다는 것이었습니다. 비록 에뮬 환경에서 일부분만 본 아이폰이지만 화면과 UI가 환상이었습니다. 에뮬레이터로 핸드폰 뽐뿌 받기는 처음이었습니다.
AND

iOS 2008. 3. 8. 09:35
어제 애플에서 아이폰의 SDK를 공개하고 로드맵을 발표했습니다. 발표후 아이폰 소프트웨어 로드맵을 알리는 애플의 메인화면뿐만 아니라 ADC의 메인 화면도 아이폰과 맥 개발로 양분된 모습을 보니 애플이 아이폰 어플리케이션 개발에 얼마나 많이 기대를 하고 신경을 쓰고 있는지 짐작할 수 있습니다.

사용자 삽입 이미지

아래는 발표 동영상중에서 몇가지 게임 시연 모습을 캡쳐한 이미지들입니다. 3D 슈팅게임에서는 아이폰을 움직이며 비행기를 조종하고 화면을 터치해 발사하는 모습이 재미있었습니다.
사용자 삽입 이미지 사용자 삽입 이미지 사용자 삽입 이미지

기억나는 것은 이미지를 편집하다 아이폰을 좌우로 흔들면 실행이 취소(Undo)되는 모습이었습니다. 별다른 버튼이 없는 아이폰이기 때문에 Undo를 실행할 수 있는 방법이 그것밖에 없을 것도 같지만 매우 인상적이었습니다. (휴~ 이 동영상을 보니 아이팟 터치에 대한 구매의욕을 겨우 잠재워 놨는데 이제 더이상 견딜 수 없게 된 것 같습니다)

한가지 흥미로운 부분은 어플리케이션 만들어 애플의 App 스토어에 등록하면, 사용자들은 아이폰이나 터치로 인터넷을 통해 App 스토어에서 필요한 어플리케이션을 검색하고 구매할 수 있습니다. 이로 인해 수익이 일어 날 경우 금액의 70%를 제작자가 받을 수 있다고 합니다.

소프트웨어를 제작만 하면 애플이 유통을 담당해주니 좋은 어플리케이션을 개발만 할 수 있다면 개인 개발자들도 수익을 올릴 수 있을 것 같습니다.

하지만 개발자가 위의 서비스를 이용할려면 아이폰 개발자 프로그램에 등록해야 하는 것 같습니다. 등록비용은 개인을 대상으로 한 Standard는 99달러, 기업을 대상으로 한 Enterprise는 299달러입니다. 현재는 미국의 개발자들만 등록할 수 있는 것 같습니다. 몇달안으로 다른 여러 나라로 확대된다고 합니다.

SDK는 아이폰 DevCenter에서 다운로드 받으실 수 있으며 크기는 2.1GB입니다. 홈페이지에는 벌써 개발에 필요한 많은 레퍼런스와 샘플코드, 강좌 동영상들의 자료들이 충분히 준비되어 있습니다. 바로 시작할 수 있습니다.

그러나!!!

사용자 삽입 이미지

"OS X 10.5(레퍼드)는 언제 써볼수 있을까..." 라면 촐싹대는 것이 아니였는데...
AND