Xcode3 에서 간단한 코코아 UI 샘플을 만들어 보는 튜토리얼입니다. 아래와 같이 하나의 입력 창과 세개의 버튼을 가진 간단한 어플리케이션 입니다.

각각의 버튼들의 기능은 아래와 같습니다. 라벨의 이동시에는 animator를 이용해 간단한 효과를 주었습니다.

텍스트에 입력된 내용을 상단의 라벨에 출력합니다.

라벨이 원래 있던 좌측의 위치로 이동하도록 합니다.

라벨이 우측 끝으로 이동하도록 합니다.


1. 프로젝트 생성
Xcode를 실행하고 메뉴에서 File/New Project...를 클릭합니다. 좌측에서 Mac OS X/Application을 선택하고 Cocoa Application 템플릿을 선택한 후, Choose... 버튼을 클릭합니다. 

(iPhone SDK 설치되어 있지 않으면 좌측의 메뉴가 조금 다를 수도 있습니다)

프로젝트가 저장될 디렉토리를 선택한 후에 프로젝트명(Control1)을 입력하고 Save 버튼을 클릭합니다.



2. 인터페이스 빌더
1) 인터페이스 빌더 실행
좌측과 같이 Xcode의 Groups & Files/Resources의 MainMenu.xib를 더블클릭하여 인터페이스 빌더를 실행합니다.







2) 라이브러리 윈도우
인터페이스 빌더의 메뉴에서 Tools/Library를 클릭하거나 단축키(Shift+Command+L)를 이용하여 아래와 같은 라이브러리 윈도우를 오픈합니다. 이 윈도우 하단의 아이템중에서 필요한 각종 오브젝트나 컨트롤들을 원하는 위치에 드래그하여 사용할 수 있습니다.



3) 윈도우
라이브러리 윈도에서 아래와 같이 Label, Text Field, Push Button을 드래그 해서 윈도우에 아래와 같이 배치하고 윈도우 크기를 적당하게 조절합니다.


라이브러리 윈도우에서 각 컨트롤의 위치는 아래와 같습니다.

  • Label - Cocoa / Views & Cell / Input & Values
  • Text Field - Cocoa / Views & Cell / Input & Values
  • Puish Button - Cocoa / Views & Cell / Buttons


3. AppController
1) 소스파일 생성
다시 Xcode로 돌아와 Groups & Files에서 Classes를 우클릭을 하거나 단축키(Command+N)를 이용하여 파일생성 창을 오픈합니다.


Mac OS X/Cocoa에서 Objective-C class를 선택하고 Next 버튼을 클릭합니다.

파일명에 AppController.m을 입력하고 Finish 버튼을 클릭합니다.

좌측과 같이 AppController.h와 AppController.m 소스 파일이 Classes 아래에 생성되었습니다.

먼저 AppController.h를 클릭하여 소스를 변경합니다.


2) AppController.h
헤더 파일에서 아래에 푸른색으로 되어 있는 부분을 입력합니다.
#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
    IBOutlet NSTextField    *textLabel;
    IBOutlet NSTextField    *inputField;
   
    IBOutlet NSButton    *refreshButton;
    IBOutlet NSButton    *leftButton;
    IBOutlet NSButton    *rightButton;

    NSPoint    originalPoint;
}

- (IBAction)setLabel:(id)sender;
- (IBAction)moveLeft:(id)sender;
- (IBAction)moveRight:(id)sender;

@end

인터페이스 빌더에서 생성한 각각의 컨트롤들을 연결하기 위해 IBOutlet 변수와 버튼을 클릭했을 경우에 처리할 IBAction 메소드를 선언합니다.

3) AppController.m
 소스코드에서 아래에 푸른색으로 되어 있는 부분을 입력합니다.
#import "AppController.h"

@implementation AppController

- (void)awakeFromNib {
   
    // 버튼 이미지를 지정
    NSImage *buttonImage = [NSImage imageNamed:NSImageNameRefreshTemplate];
    [refreshButton setImage:buttonImage];

    buttonImage = [NSImage imageNamed:NSImageNameGoLeftTemplate];
    [leftButton setImage:buttonImage];
   
    buttonImage = [NSImage imageNamed:NSImageNameGoRightTemplate];
    [rightButton setImage:buttonImage];
   
    originalPoint = [textLabel frame].origin;
}

- (IBAction)setLabel:(id)sender {
   
    // 입력한 문자를 라벨에 지정하고 라벨의 크기를 문자열의 길이만큼 변경
    [textLabel setStringValue:[inputField stringValue]];   
    [textLabel sizeToFit];
}

- (IBAction)moveLeft:(id)sender {
   
    // 라벨을 초기 위치로 이동
    [[textLabel animator] setFrameOrigin:originalPoint];
}

- (IBAction)moveRight:(id)sender {
    CGFloat xPos;
   
    // 우측 모서리로 이동하도록 윈도우의 크기에서 라벨의 크기를 뺀 위치로 설정
    xPos = [[[NSApp mainWindow] contentView] bounds].size.width;
    xPos -= [textLabel frame].size.width;
   
    // 에니메이션과 함께 라벨의 위치 변경
    NSPoint targetPoint = NSMakePoint(xPos, originalPoint.y);
    [[textLabel animator] setFrameOrigin:targetPoint];
}

@end


4. 연결
다시 인터페이스 빌더로 돌아 가서 작업을 마무리 합니다

1) AppController 인스턴스 생성
라이브러리 윈도우에서 좌측과 같은 Object를 MainMenu.xib 윈도우로 드래그해서 가져다 놓습니다.

Object는 라이브러리 윈도우에서 Cocoa / Objects & Controllers / Controllers내에 있습니다.

아래와 같이 Object가 생성된 것을 확인하시고 Object가 선택된 상태에서 인터페이스 빌더의 Tools/Inspector 메뉴를 클릭하거나 단축키 (Shift + Command + I)를 이용하여 Inspector 창을 오픈합니다.

Inspector 창에서 좌측과 같이 6번째에 있는 App Controller Identity를 선택합니다. (단축키: Command + 6)

Class메뉴의 콤보박스를 클릭하고 Xcode에서 이전 단계에서 생성한 AppController를 선택합니다.


2) 연결
MainMenu.xib 창에서 App Controller를 선택한 상태에서 마우스 우클릭을 하면 아래와 같이 연결 창이 오픈됩니다.


연결창의 각 항목 좌측에 있는 원 모양의 영역에 마우스를 가져가면 우측과 같이 '+' 모양으로 변경됩니다. 이 상태에서 마우스로 '+'를 클릭한 후에 드래그 하여 각각의 해당 항목들에 연결합니다.

마우스를 클릭한 상태에서 파란색 선을 연결할 컨트롤로 가져가면 아래와 같이 컨트롤의 이름과 함께 선택되었을 때 마우스 클릭을 해제합니다.


이런 방식으로 Outlet과 Action을 각각의 컨트롤들과 연결합니다.

  • Outlets / inputField -> Text Field 
  • Outlets / textLabel -> Label
  • Outlets / refreshButton -> Button (첫번째)
  • Outlets / leftButton -> Button (두번째)
  • Outlets / rightButton -> Button (세번째)

  • Received Actions / setLabel -> 버튼 (첫번째)
  • Received Actions / moveLeft -> 버튼 (두번째)
  • Received Actions / moveRight -> 버튼 (세번째)

작업이 완료되면 아래와 같이 연결되었는지 확인합니다.

 
3) 빌드 및 테스트
이제 모든 작업이 완료되었습니다. 인터페이스 빌더와 Xcode에서 변경된 내용을 저장하고, Xcode에서 좌측의 툴바를 클릭하거나 단축키(command + R)를 이용하여 빌드하고 테스트 해 봅니다.


Xcode를 처음 해보시는 분들도 따라 하실 수 있게 최대한 자세하게 설명을 할려고 했습니다. 하지만 제 생각대로 처음 하시는 분들도 무리없이 하실 수 있을지는 잘 모르겠습니다.

잘 안되시는 분들은 아래의 프로젝트 파일을 다운로드 받아서 비교해 보시기 바랍니다.

'Xcode 3 > 튜토리얼' 카테고리의 다른 글

2. 간단한 Core Data 예제  (9) 2008.10.05
1. Xcode3 첫 어플리케이션  (17) 2008.09.26

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

  • << : 한달 전으로 이동
  • < : 하루 전으로 이동
  • > : 하루 후로 이동
  • >> : 한달 후로 이동
  • 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

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

간단하게 아이폰용 바이오리듬 어플리케이션을 만들어 보겠습니다. 저도 처음 아이폰 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들이 멋있어서 맞출려면 디자인이나 이미지에 신경을 써야 할 것 같습니다. 나중에 조금 더 알아본 후에 월별로 출력하도록 수정하고 몇가지 변경해 본 후에 다시 한번 올려 보겠습니다.

이번 장에서는 빙글빙글 회전하는 표적에 마우스를 클릭하여 표적의 1, 2, 3, 4의 번호 중 하나를 선택하는 게임을 만들어 보겠습니다.
사용자 삽입 이미지
1. 스프라이트 추가

사용자 삽입 이미지
기존 스프라이트를 삭제하고 좌측의 스프라이트 추가 버튼을 클릭하여 표적과 화살 이미지를 선택하여 아래와 같이 두개의 스프라이트를 추가합니다. 표적과 화살 이미지는 페인트 툴에서 직접 그리거나 아래의 이미지 파일을 다운로드 받아 사용합니다.


2. 표적 스크립트

1) 변수 생성
변수(Variables)는 스크립트에서 특정 상태나 값을 저장하기 위해서 사용합니다. 변수는 말 그대로 변할 수 있는 수(값)이기 때문에 원하는 값을 설정하고 참조할 수 있습니다. 이 프로그램에서는 표적이 회전해야 하는지 화살이 발사되어 멈추어야 하는지를 판별하기 위해 isEnd란 변수를 생성하여 사용합니다.

isEnd가 1일 경우에는 사용자가 마우스를 클릭하여 화살이 발사된 경우며, 0일 경우에는 아직 발사하지 않은 상태입니다.

사용자 삽입 이미지
변수를 만들기 위해 Variables를 항목에서 [Make a variable] 항목을 클릭합니다.

아래와 같이 변수를 생성하는 창이 열리면, 생성될 변수의 이름을 입력한 후 [OK]를 클릭합니다.

For all sprites는 모든 스프라이트가 사용하도록 생성됩니다. For this sprite only는 해당 스프라이트에서만 사용하는 변수입니다. 여기서는 표적 스프라이트와 화살 스프라이트에서 모두 참조 하므로 아래와 같이 For all sprites를 선택합니다.

사용자 삽입 이미지

사용자 삽입 이미지
변수 생성이 완료되면 좌측과 같이 세개의 블럭이 추가됩니다. 각 블럭들의 의미는 다음과 같습니다.




[change 변수 by (값)]
변수에 (값)에 있는 수만큼 더 합니다.

[set 변수명 to (값)]
변수를 (값)에 입력된 값을 대입합니다.

[변수명]
변수를 if와 같은 조건 블럭이나 Numbers의 여러 대입, 비교등의 블럭내에 드래그 하여 가져다 놓을 수 있습니다.

사용자 삽입 이미지
앞의 체크박스를 클릭하면 좌측과 같이 실행화면에서 좌측과 같이 변수의 값을 확인할 수 있습니다.

2) 스프라이트
표적을 회전시키기 위해 해당 블럭들을 가져와서 아래와 같이 배치합니다.

사용자 삽입 이미지

[set isEnd to (0)]
시작시에 isEnd 값을 0으로 설정합니다.

[forever if <isEnd = (0)>]
포함된 명령어 블럭들이 isEnd가 0인 동안 무한으로 반복됩니다. 녹색의 <() = ()> 블럭은 Numbers에 위치합니다.

[turn (15) degrees]
표적을 한번 반복될 때마다 15도씩 시계방향으로 회전시킵니다.

3. 화살 스크립트

마우스가 클릭되었을 때 화살을 마우스가 눌려진 위치에 출력하고 표적을 정지 시키기 위해 아래와 같이 명령어 블럭들을 배치 합니다.

사용자 삽입 이미지

[hide]
시작시에는 화살의 모습을 숨깁니다.

if <<isEnd = 0> AND <mouse down?>>]
isEnd 변수가 0(표적이 회전 중)이고 사용자의 마우스가 눌러 졌을 경우에만 화살을 발사하도록 합니다. <> and <> 블록은 좌측과 우측의 값이 모두 참(1)일 경우에만 참(1)이 됩니다.

[set isEnd to (1)]
화살이 발사 되었으므로 isEnd를 1로 설정합니다. isEnd가 1이므로 표적이 회전하지 않습니다. (상단 표적 스프라이트 참조)

[play sound [pop]]
스프라이트가 기본으로 가지고 있는 소리(pop)를 출력합니다.

[go to [mouse-pointer]]
마우스 포인터가 위치한 곳으로 스프라이트의 위치를 이동합니다.

[show]
이제 화살을 출력합니다.

이제 테스트를 위해 시작버튼을 클릭합니다. 회전하는 표적에 마우스를 가져가서 클릭하면 해당 위치에 화살이 출력되며 표적이 멈춥니다.



이번장에서는 신나는 음악에 맞추어 케릭터가 춤을 추는 에니메이션을 만들어 보겠습니다. 연속적으로 케릭터가 동작하는 방법과 스크래치에 포함된 케릭터, 배경 이미지와 음악을 활용하는 방법을 알아 보겠습니다.   

사용자 삽입 이미지

1. 배경화면 설정
스크래치는 페인트 툴과 녹음 기능을 이용하여 원하는 이미지와 효과음을 직접 만들어 사용할 수도 있고, 존재하는 파일을 불러 와서 사용할 수 있습니다. 이와 함께 사용자가 스크래치에 이미 준비되어 있는 각종 그림과 음악 파일들을 사용할 수 있습니다.

스크래치가 설치된 폴더를 보면 Media란 폴더가 있습니다. 이곳에는 배경과 커스튬에 사용할 수 있는 이미지 파일과 효과음과 배경음으로 사용할 수 있는 음악 파일들이 있습니다. 이곳에 있는 파일들을 이용하면 손쉽게 원하는 스크래치 프로그램들을 만들 수 있습니다.

1) 배경화면 설정
사용자 삽입 이미지
스크래치 우측하단의 Stage의 사각형 부분을 클릭하면 Backgrounds란 텝이 나타납니다. New background:에서 [Import] 버튼을 클릭합니다.

Backgrounds/Indoors 폴더를 선택합니다. spotlight-stage 이미지 파일을 선택하고 [OK] 버튼을 클릭합니다. 기존의 background1로 되어 있는 파일은 (X) 버튼을 클릭하여 삭제합니다.

사용자 삽입 이미지

2. 커스텀 설정

1) 기본 스프라이트 삭제
사용자 삽입 이미지
새로운 스크래치 프로젝트를 만들면 좌측과 같이 스크래치의 기본 캐릭터가 Sprite1로 설정되어 있습니다.

 기본 캐릭터에 마우스 오른쪽 버튼을 클릭하여 나오는 메뉴에서 delete를 클릭하여 삭제합니다.


2) 스프라이트 추가
사용자 삽입 이미지
좌측과 같은 우측 중간에 위치해 있는 버튼을 클릭하여 스프라이트를 파일로 부터 추가합니다. 아래와 같은 창이 뜨면 Costumes/People에서 breakdancer-1이란 스프라이트를 추가합니다.

사용자 삽입 이미지

3) 커스튬 추가
춤추는 에니메이션을 위해서 기본 스프라이트에  커스튬을 추가합니다. 스프라이트의 Custumes 탭에서 New costume의 [Import] 버튼을 클릭하여 breakdance-2부터 4까지 파일을 아래와 같이 추가 합니다.
사용자 삽입 이미지

3. 배경음 설정
Stage가 선택된 상태에서 아래와 같이 Sounds탭을 선택한 후에  New sound:의 [Import] 버튼을 클릭합니다. 창이 뜨면 Sounds/Music Loops밑의 Drum을 선택합니다. 아래와 같이 Drum 사운드 파일이 등록되었음을 확인합니다.

사용자 삽입 이미지

4. 스크립트

1) 에니메이션
스크립트 블럭을 가지고 와서 아래와 같이 배치합니다.
사용자 삽입 이미지

[switch to costume (custume)]은 현재 스프라이트의 커스튬(이미지)를 설정된 커스튬으로 변경해 주는 명령어 입니다. 아래와 같이 화살표를 클릭하면 현재 스프라이트에 등록된 custume을 선택할 수 있습니다.
사용자 삽입 이미지

[repeat (5)]는 블럭안의 명령어들이 ()안에 지정된 숫자만큼 반복해서 실행됩니다. [wait (0.5) secs]는 ()안에 지정된 초만큼 기다렸다 다음 블럭이 실행됩니다.

여기선 custume1을 보여 주고 0.5초씩 멈추어 가며  2, 3, 4 custume을 반복해서 5회 보여 준 후에 다시 custume1 부터 반복해서 보여주게 만들어 줍니다. 이 명령어들로 케릭터가 반복적으로 춤을 추는 것처럼 보이는 에니메이션을 만듭니다.

2) 배경음악 출력
Stage를 클릭하고 Scripts에서 아래와 같이 블럭을 배치 합니다.

사용자 삽입 이미지

[forever]로 배경음악을 반복해서 연주 하도록 합니다. [play sound (Drum) until done]은 음악이 끝날 때까지 다음 명령어가 실행되지 않습니다. [play sound (Drum)]은 연주가 끝나지 않더라도 계속 연주를 하기 때문에 겹쳐서 들리게 됩니다.

이제 깃발 버튼(Start)을 클릭하여 실행하여 봅니다. 음악에 맞추어 브레이크 댄스를 추는 모습을 볼 수 있습니다.



음악파일을 연주하는 간단한 스크래치 프로젝트를 시작해 보겠습니다. 완성된 모습은 제 스크래치 프로젝트 페이지에서 확인하실 수 있습니다.

사용자 삽입 이미지

모양은 위와 같습니다. 음악이 연주되면 좌측과 같이 플레이 아이콘이 표시되며 음악이 중지되면 우측과 같은 중지 아이콘이 표시됩니다. 입력은 스페이스 키를 받으며 누를 때마다 시작, 중지가 번갈아 가며 실행됩니다.

예제로 사용된 MP3에 대하여...



1. 이미지 파일 생성

1) 기본 스프라이트 삭제
사용자 삽입 이미지
먼저 스크래치를 실행합니다. 좌측과 같이 기본 스프라이트가 있습니다.

우측 하단의 기본 스프라이트를 마우스로 우클릭하면 좌측과 같은 메뉴가 나옵니다. 이 중 delete를 클릭하여 기본 스프라이트를 삭제합니다.


2) 스프라이트 생성
스프라이트를 생성하는 방법은 두 가지가 있습니다. 페인트 에디터를 이용해서 새로 그리는 방법과 기존의 이미지 파일에서 불러 오는 방법이 있습니다.(기본 이미중에 하나를 무작위로 불러와서 생성하는 한 가지 방법이 더 있지만, 여기서는 제외합니다.)

사용자 삽입 이미지
우측 스테이지 하단을 보시면 좌측과 같은 버튼이 두 개 있습니다. 그 중 좌측의 버튼이 페인트 에디터를 이용하여 스프라이트를 생성하는 버튼이며, 우측의 버튼이 기존의 이미지 파일에서 불러와 스프라이트를 생성하는 버튼입니다.

아래의 페인트 에디터로 직접 그려서 스프라이트를 생성하는 a 방법과 기존의 이미지 파일에서 불러오는 b 방법 중에 하나를 선택하셔서 진행하시면 됩니다.

a. 페인트 에디터 이용

사용자 삽입 이미지
먼저 페인트 에디터를 사용하여 직접 스프라이트를 그려 보겠습니다. 좌측의 버튼을 클릭하여 아래와 같이 페인트 에디터를 엽니다. 각종 그리기 툴들을 이용하여 적당한 플레이 아이콘을 그린 후에 완료되면 아래의 [OK] 버튼을 클릭하여 저장합니다.

사용자 삽입 이미지

사용자 삽입 이미지
좌측과 같이 스프라이트가 새로 만들어졌습니다. 기본으로 'Costume1'로 되어 있는 부분을 마우스로 클릭하여 입력이 가능하도록 합니다. 아래와 같이 이름을 'play'로 변경합니다.

사용자 삽입 이미지






이제 정지된 모양을 추가하겠습니다. 같은 스프라이트에서 모양(costume)이 추가될 때는 [Paint] 또는 [Import] 버튼을 이용합니다.

사용자 삽입 이미지
[Paint] 버튼을 클릭하여 다시 페인트 에디터를 오픈하여 위와 같은 방법으로 종료 버튼을 그린 후에 [OK]를 클릭합니다.

사용자 삽입 이미지
play와 동일한 방법으로 이름을 'stop'으로 변경하여 줍니다.

좌측과 같이 각각의 모양들이 'play'와 'stop'이란 이름으로 저장되어 있음을 확인합니다.

이와 같이 페인트 에디터로 직접 스프라이트를 만들었으면 아래의 'b. 이미지 파일에서 불러 오기'는 건너 뛰고, 2.음악파일 등록으로 진행합니다.




b. 이미지 파일에서 불러 오기

이미지는 기존에 가지고 계신 이미지나 제가 만든 아래의 이미지를 다운로드 하셔서 압축을 푸시고 사용하시면 됩니다.

사용자 삽입 이미지
스테이지 하단의 좌측과 같은 볼러오기 버튼을 클릭합니다. 아래와 같이 파일선택 창이 나오면 play 이미지를 선택하고 [OK] 버튼을 클릭합니다. 스크래치에서는 jpg, bmp, png, gif 형식의 이미지 파일을 사용하실 수 있습니다.

사용자 삽입 이미지

사용자 삽입 이미지
다시 스프라이트 창으로 돌아와 Stop 이미지를 위해 좌측과 같이 [Import] 버튼을 클릭합니다. 위와 같이 파일선택 창이 나오면 정지 아이콘으로 사용할 stop 이미지를 선택하고 [OK] 버튼을 클릭합니다.

사용자 삽입 이미지
위의 a.페인트 에디터와 같은 방법으로 각 모양(costume)의 이름을 'play'와 'stop'으로 변경합니다.

이제 좌측과 같이 설정되어 있음을 확인합니다.









2. 음악 파일 등록

1) 음악 파일 불러 오기
사용자 삽입 이미지
이제 연주 할 음악파일을 선택합니다. 음악 파일은 mp3를 기본으로 wav, wma, aif, au 파일을 지원합니다.

좌측과 같이 Sounds 탭을 클릭하여 [Import] 버튼을 클릭합니다. 파일 선택창에서 플레이할 음악파일을 선택한 다음 [OK] 버튼을 클릭합니다.


2) 이름 변경
사용자 삽입 이미지
스프라이트와 같은 방법으로 마우스로 클릭한 후에 파일명으로 되어 있는 이름을 좌측과 같이 'myMusic'으로 이름을 변경합니다.



3. 스크립트 생성

1) 변수 isPlay 생성
현재 상태가 연주 중인지 정지 중인지를 알기 위해서 'isPlay'라는 변수를 하나 생성합니다. 이 변수가 '1'일때는 연주 중이며 '0'일 때는 정지 중을 나타냅니다.

사용자 삽입 이미지
좌측과 같이 붉은 색 [Varialbles] 버튼을 클릭한 후에 아래에 있는 [Make a variable]을 클릭합니다.




사용자 삽입 이미지
좌측과 같은 창이 나오면 변수명에 'isPlay"'를 입력하고 [OK] 버튼을 클릭합니다.

아래와 같이 isPlay 변수가 생겼음을 확인합니다.





사용자 삽입 이미지
[change isPlay by (1)] 현재 isPlay의 값에서 ()안의 수만큼 더합니다. ()안의 수를 -로 입력하면 그 수만큼 빼게 됩니다.

[set isPlay to (0)] isPlay의 값을 ()의 수로 설정합니다. 이 명령어 블록을 그대로 가져가면 isPlay는 0으로 설정됩니다.

[[v] isPlay] 변수이며 비교 및 사용시에 스크립트 영역으로 드래그해서 사용합니다. 체크[v]되면 스테이지에서 보이게 됩니다.

2) 명령어 블록 배치


a. 블럭 배치 방법
사용자 삽입 이미지
사용자 삽입 이미지
을 클릭한 후에 좌측과 같이 [When (flag) checked]를 마우스로 클릭한채로 Scripts 영역으로 드래그 하여 가져다 놓습니다.

삭제시에는 반대로 스크립트 영역 내의 블럭을 드래그 하여 블럭들이 있는 창으로 가져다 놓습니다.

사용자 삽입 이미지
스크립트 영역에서 여러 블럭들을 연결할 때는  해당 블럭을 드래그 하여 연결할 곳으로 가져 갑니다.

연결이 가능하면 좌측과 같이 붉은색 화살표가 가르키는 것과 같은 흰색 마크가 생깁니다. 여기서 마우스 버튼을 놓으면 그 위치에 블럭이 위치합니다.

b. 속성 변경 방법
사용자 삽입 이미지
좌측과 같이 블럭 중에서  하얀 배경색으로 되어 있는 부분은 마우스로 클릭 후에 키보드 입력으로 원하는 값으로 변경할 수 있습니다.

사용자 삽입 이미지
아래로 화살표가 있는 부분은 마우스를 클릭하면  선택 가능한 목록들이 표시됩니다. 마우스로 목록 중에서 선택될 부분을 클릭합니다.


c. 블럭 배치
이제 블럭들을 배치해 보겠습니다. 각각의 블럭들 항목에서 가져다가 스크립트 창에 블럭들을 아래와 같이 연결합니다.

사용자 삽입 이미지

[Control]에서 [when (flag) clicked]를 가져 옵니다. 사용자가 시작 버튼을 클릭하면 실행되는 부분입니다. 여기서 초기 시작시 필요한 행동이나 초기값을 설정할 수 있습니다.

[Variables]에서 [set isPlay to (0)]을 가져 옵니다. 처음에는 음악이 연주되지 않기 때문에 'isPlay' 변수 값을 '0'으로 설정합니다.

[Looks]에서 [switch to costume [stop]]을 가져 옵니다. 위와 마찬가지 이유로 정지 이미지가 표시되도록 합니다.

이제 음악의 연주와 중지를 위해 아래와 같이 각각의 블럭들을 가져와서 배치하고 설정합니다. 블럭들의 색을 참조하시면 블럭들이 포함된 위치를 알 수 있습니다.

사용자 삽입 이미지

각각의 스크립트 블록 명력들은 아래와 같은 동작됩니다.

[When [space] key pressed] 스페이스키가 눌러 졌을 때 아래에 연결된 블럭들이 실행됩니다.

[if <(isPlay) = 0>] 만약 현재 상태가 정지중이면 포함된 명령을 실행합니다.
[set isPlay to (1)]  현재 상태를 연주중으로 설정합니다.
[play sound [myMusic]] myMusic을 연주 합니다.
[switch to costume[play]] 버튼 모양을 연주중으로 변경합니다.

[else] 만약 현재 상태가 정지중이 아니면 포함된 명령을 실행합니다.
[set isPlay to (0)] 현재 상태를 정지로 설정합니다.
[stop all sounds] 연주를 중지 합니다.
[switch to costume[stop]] 버튼 모양을 중지로 변경합니다.


4. 실행 및 저장

1) 실행
사용자 삽입 이미지

사용자 삽입 이미지
좌측의 시작 버튼을 클릭합니다. 키보드에서 Space 키를 입력하시면 아이콘이 플레이 모양으로 변경되면서 등록한 음악이 연주가 됩니다. 중지 시에는 다시 Space키를 입력합니다.


2) 프로젝트 저장

사용자 삽입 이미지
툴바에서 [Save] 버튼을 클릭하여 지금까지 작업한 내용을 저장합니다. 저장된 파일은 [Open]을 이용해서 이후에 다시 불러 올 수 있습니다.

사용자 삽입 이미지

Project author 항목에는 제작자 이름을 입력합니다. About this project에는 이 프로젝트에 대한 설명을 입력합니다. 위의 두 입력은 선택사항이며 입력하지 않아도 무방합니다.

마지막으로 New Filename에 프로젝트 명을 입력하고 [OK]를 클릭하여 저장합니다.
(모든 입력창에서 한글은 입력되지 않습니다.)
스크래치로 음악을 연주하는 간단한 프로그램을 만들어 보았습니다. 다음 장에선 스프라이트를 움직이는 간단한 프로젝트를 만들어 보겠습니다.