/**
 *  @file SurfaceView.h
 *  A View that implements a CoreSurface buffer
 *  The buffer is created automatically in initWithFrame: with the size
 *  passed to it and 5-6-5 RGB format.
 *  The memory can be accessed with the pixels property, and after modifying
 *  it you must call setNeedsDisplay on it for it to update.
 */
 
#import <UIKit/UIKit.h>
#import <CoreSurface/CoreSurface.h>
 
@interface SurfaceView : UIView
{
    CALayer *surfaceLayer;
    unsigned short *pixels;
    CoreSurfaceBufferRef surfaceBuffer;
}
 
@property (nonatomic, readonly) unsigned short *pixels;
 
@end
 
 
 
/**
 *  @file SurfaceView.m
 *  Implementation of SurfaceView
 */
 
#import "SurfaceView.h"
 
@implementation SurfaceView
 
- (id)initWithFrame:(CGRect)frame {
    frame.origin.x /= 2;
    frame.origin.y /= 2;
   
    if (self = [super initWithFrame:frame]) {
        // Create Surface
        int w = frame.size.width, h = frame.size.height;
        surfaceBuffer = CoreSurfaceBufferCreate((CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:w],         kCoreSurfaceBufferWidth,
            [NSNumber numberWithInt:h],         kCoreSurfaceBufferHeight,
            [NSNumber numberWithInt:'L565'],    kCoreSurfaceBufferPixelFormat,
            [NSNumber numberWithInt:2*w*h],     kCoreSurfaceBufferAllocSize,
            [NSNumber numberWithBool:YES],      kCoreSurfaceBufferGlobal,
            [NSNumber numberWithInt:2*w],       kCoreSurfaceBufferPitch,
            @"PurpleGFXMem",                    kCoreSurfaceBufferMemoryRegion,
            nil]);
       
        // Create layer for surface
        CGRect frame2 = CGRectMake(frame.origin.x, frame.origin.y,
                                   frame.size.width+1, frame.size.height+1);
        CoreSurfaceBufferLock(surfaceBuffer, 3);
        surfaceLayer = [[CALayer layer] retain];
        [surfaceLayer setMagnificationFilter:0];
        [surfaceLayer setEdgeAntialiasingMask:0];
        [surfaceLayer setFrame:frame2];
        [surfaceLayer setContents:surfaceBuffer];
        [surfaceLayer setOpaque:YES];
        [[self _layer] addSublayer:surfaceLayer];
        CoreSurfaceBufferUnlock(surfaceBuffer);
       
        // Get base address
        pixels = CoreSurfaceBufferGetBaseAddress(surfaceBuffer);
    }
    return self;
}
 
- (void)dealloc {
    [surfaceLayer release];
    [super dealloc];
}
 
- (void)drawRect:(CGRect)rect {
    // It will break if you remove this empty function
}
 
- (unsigned short*)pixels {
    return pixels;
}
@end

/** 
 *  @file SurfaceView.h
 *  A View that implements a CoreSurface buffer
 *  The buffer is created automatically in initWithFrame: with the size
 *  passed to it and 5-6-5 RGB format.
 *  The memory can be accessed with the pixels property, and after modifying
 *  it you must call setNeedsDisplay on it for it to update.
 */
 
#import <UIKit/UIKit.h>
#import <CoreSurface/CoreSurface.h>

@interface SurfaceView : UIView 
{
    CALayer *surfaceLayer;
    unsigned short *pixels;
    CoreSurfaceBufferRef surfaceBuffer;
}

@property (nonatomic, readonly) unsigned short *pixels;

@end



/** 
 *  @file SurfaceView.m
 *  Implementation of SurfaceView
 */
 
#import "SurfaceView.h"

@implementation SurfaceView

- (id)initWithFrame:(CGRect)frame {
    frame.origin.x /= 2;
    frame.origin.y /= 2;
    
    if (self = [super initWithFrame:frame]) {
        // Create Surface
        int w = frame.size.width, h = frame.size.height;
        surfaceBuffer = CoreSurfaceBufferCreate((CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:w],         kCoreSurfaceBufferWidth,
            [NSNumber numberWithInt:h],         kCoreSurfaceBufferHeight,
            [NSNumber numberWithInt:'L565'],    kCoreSurfaceBufferPixelFormat,
            [NSNumber numberWithInt:2*w*h],     kCoreSurfaceBufferAllocSize,
            [NSNumber numberWithBool:YES],      kCoreSurfaceBufferGlobal,
            [NSNumber numberWithInt:2*w],       kCoreSurfaceBufferPitch,
            @"PurpleGFXMem",                    kCoreSurfaceBufferMemoryRegion,
            nil]);
        
        // Create layer for surface
        CGRect frame2 = CGRectMake(frame.origin.x, frame.origin.y, 
                                   frame.size.width+1, frame.size.height+1);
        CoreSurfaceBufferLock(surfaceBuffer, 3);
        surfaceLayer = [[CALayer layer] retain];
        [surfaceLayer setMagnificationFilter:0];
        [surfaceLayer setEdgeAntialiasingMask:0];
        [surfaceLayer setFrame:frame2];
        [surfaceLayer setContents:surfaceBuffer];
        [surfaceLayer setOpaque:YES];
        [[self _layer] addSublayer:surfaceLayer];
        CoreSurfaceBufferUnlock(surfaceBuffer);
        
        // Get base address
        pixels = CoreSurfaceBufferGetBaseAddress(surfaceBuffer);
    }
    return self;
}

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

- (void)drawRect:(CGRect)rect {
    // It will break if you remove this empty function
}

- (unsigned short*)pixels {
    return pixels;
}
@end
* Quartz 라이브러리

  - 아이폰 OS에서 그래픽 기능을 구현하기 위한 C 라이브러리

  - 그림을 그리기 위해서는 그림을 그릴 대상인 CGContextRef가 필요하다. 그림을 그리기 위한 모든 정보를 저장하는 컨텍스트임. 그래픽카드를 추상화한 개념이라고 생각하면 된다

view : drawRect( ) -> CGContextRef에서 상속받음

  - Quartz API를 이용해 객체를 생성, 뷰가 아닌 다른 곳에 그림을 그릴 수 있다

  - 뷰에 그림을 그리기 위해서는 drawRect 메소드를 이용해서

CGContextRef 변수명 = UIGraphicsGetCurrentContext();

    와 같이 해주면 변수명이 CGContextRef 객체가 된다. 

  - touchesBegan 등에서도 그릴 수는 있다. 다만 CGContextRef가 없기 때문에 drawRect()를 빌려서 사용해야 한다


* drawRect

  - 다음과 같은 경우에 자동으로 호출된다
뷰 위를 부분적으로 덮고 있던 또 다른 뷰가 이동하거나 사라질때
숨겨져 있던 뷰의 hidden 속성을 NO로 설정해서 다시 나타나도록 했을 때
뷰를 스크린 밖으로 스크롤했다가 돌아올때
setNeedsDisplay나 setNeedsDisplayInRect를 호출했을 때

  - 패러미터
CGContextSetRGBStrokeColor(그래픽컨텍스트,R,G,B,A):선의 색상
CGContextSetLineWidth(그래픽컨텍스트, 두께)
CGContextStrokePath(그래픽 컨텍스트)
CGContextSetRGBFillColor(그래픽컨텍스트,R,G,B,A): 면색


* 작도 함수

  - 선 그리기
CGContextMoveToPoint(그래픽컨텍스트객체, x, y)
CGContextAddLineToPoint(그래픽컨텍스트객체, x, y) : 계속해서 이어서 그린다
CGContextSetLineDash(그래픽컨텍스트객체, 시작위치, 실수배열, 개수);

  - 사각형 그리기
CGContextFillRect(그래픽컨텍스트객체, CGRect 타입 변수)

  - 원 그리기
CGContextAddEllipseInRect(그래픽컨텍스트객체, CGRect 타입 변수)

  - 다각형은 선을 연속해서 그리면 됨


* 선 그리는 예제 코드(TestView.m)

#import "TestView.h"

@implementation TestView

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

-(IBAction)Line
{
mode = 1;
[self setNeedsDisplay];
}

-(IBAction)Rect
{
mode = 2;
[self setNeedsDisplay];
}

-(IBAction)Polygon
{
mode = 3;
[self setNeedsDisplay];
}

-(void) drawLine:(CGContextRef)context 
{
// 선 색상 설정
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);

// 선 두께
CGContextSetLineWidth(context, 1.0);
// 선 그리기
CGContextMoveToPoint(context, 10.0, 30.0);
CGContextAddLineToPoint(context, 310.0, 30.0);
CGContextStrokePath(context);
CGContextSetLineWidth(context, 1.0);
// 두번째 선
CGContextSetLineWidth(context, 4.0);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextMoveToPoint(context, 10.0, 50.0);
CGContextAddLineToPoint(context, 310.0, 50.0);
CGContextStrokePath(context);
// 점선
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGFloat dash[] = {5.0, 10.0, 15.0, 20.0};
CGContextSetLineDash(context, 0.0, dash, 4);
CGContextMoveToPoint(context, 10.0, 70.0);
CGContextAddLineToPoint(context, 310.0, 70.0);
CGContextStrokePath(context);
CGContextSetLineDash(context, 0.0, NULL, 0); // 점선을 그리게 해주는 방법
// 배열 이용하기
CGPoint lines[] =
{
CGPointMake(10.0, 90.0),
CGPointMake(60.0, 140.0),
CGPointMake(110.0, 130.0),
CGPointMake(160.0, 190.0),
CGPointMake(210.0, 200.0),
CGPointMake(260.0, 90.0),
CGPointMake(310.0, 200.0),
};
CGContextSetLineWidth(context, 5.0);
CGContextAddLines(context, lines, sizeof(lines)/sizeof(lines[0])); // 배열의 갯수 구하는 방법이지만 
                                                                                                      // 포인터 배열에서는 사용하지 말 것.
CGContextStrokePath(context);
}

-(void) drawRectangle:(CGContextRef)context 
{
// 선 색상
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);

// 채우기 색
CGContextSetRGBFillColor(context, 0.0, 1.0, 0.0, 1.0);
// 선 두께
CGContextSetLineWidth(context, 2.0);
// 경로에 사각형 추가
CGContextAddRect(context, CGRectMake(30.0, 30.0, 60.0, 60.0));
CGContextStrokePath(context);
// 사각형 채우기
CGContextFillRect(context, CGRectMake(130.0, 30.0, 60.0, 60.0));
CGContextStrokeRectWithWidth(context, CGRectMake(230.0, 30.0, 60.0, 60.0), 4.0);
}

-(void) drawPolygon:(CGContextRef)context 
{
CGPoint center;
// 선 색상
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);

// 채우기 색
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
// 선 두께
CGContextSetLineWidth(context, 1.0);
// 별 그리기
center = CGPointMake(80.0, 100.0);
CGContextMoveToPoint(context, center.x, center.y + 30.0);
for(int i = 1; i < 5; ++i)
{
CGFloat x = 30.0 * sinf(i * 4.0 * M_PI / 5.0); // M_PI -> Objective-C에서 매크로로 정의해둔 
        // 원주율 값
CGFloat y = 30.0 * cosf(i * 4.0 * M_PI / 5.0);
CGContextAddLineToPoint(context, center.x + x, center.y + y);
}
CGContextClosePath(context);
CGContextFillPath(context);
// 별 + 원 그리기
center = CGPointMake(160.0, 100.0);
CGContextMoveToPoint(context, center.x, center.y + 30.0);
for(int i = 1; i < 5; ++i)
{
CGFloat x = 30.0 * sinf(i * 4.0 * M_PI / 5.0);
CGFloat y = 30.0 * cosf(i * 4.0 * M_PI / 5.0);
CGContextAddLineToPoint(context, center.x + x, center.y + y);
}
CGContextAddEllipseInRect(context,CGRectMake(130, 70, 60,60));
CGContextClosePath(context);
CGContextEOFillPath(context);
// 5각형 그리기
center = CGPointMake(240.0, 100.0);
CGContextMoveToPoint(context, center.x, center.y + 30.0);
for(int i = 1; i < 6; ++i)
{
CGFloat x = 30.0 * sinf(i * 2.0 * M_PI / 6.0);
CGFloat y = 30.0 * cosf(i * 2.0 * M_PI / 6.0);
CGContextAddLineToPoint(context, center.x + x, center.y + y);
}
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}

- (void)drawRect:(CGRect)rect 
{
CGContextRef context = UIGraphicsGetCurrentContext();
if(mode==1)
[self drawLine:context];
else if(mode==2)
[self drawRectangle:context];
else
[self drawPolygon:context];
    // Drawing code
}


* 베지어 곡선

  - CGContextAddCurveToPoint 를 이용해서 그린다

  - 3개의 좌표를 대입하게 되는데 이 좌표는 첫번째 제어점, 두번째 제어점, 종료점이다

1. 앞의 예제에 툴바 버튼을 1개 추가

2. IBAction 메소드 추가하고 앞에 만든 툴바 버튼과 연결
-(IBAction)Bazier;

3. 메소드 구현

-(IBAction)Bazier
{
mode = 4;
[self setNeedsDisplay];
}

-(void) drawBezier:(CGContextRef)context 
{
// 색상 설정
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// 선 두께
CGContextSetLineWidth(context, 2.0);
// 원 그리기 (하단)
CGContextAddEllipseInRect(context,CGRectMake(60, 20, 200,200));
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextDrawPath(context, kCGPathFillStroke);
CGContextStrokePath(context);
// 베지어 곡선 그리기
CGPoint s = CGPointMake(60.0, 120.0);
CGPoint cp1 = CGPointMake(130.0, 230.0);
CGPoint cp2 = CGPointMake(190.0, 10.0);
CGPoint e = CGPointMake(260.0, 120.0);
CGContextMoveToPoint(context, s.x, s.y);
CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
CGContextSaveGState(context);
// 반원 그리기 (상단)
CGContextAddArc(context,160,120,100,0,M_PI,true);
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextDrawPath(context, kCGPathFillStroke);
CGContextStrokePath(context);
}

- (void)drawRect:(CGRect)rect 
{
CGContextRef context = UIGraphicsGetCurrentContext();
if(mode==1)
[self drawLine:context];
else if(mode==2)
[self drawRectangle:context];
else if(mode ==3)
[self drawPolygon:context];
else
[self drawBezier:context];
    // Drawing code
}


* Core Animation

  - 간단 예제
#import <QuartzCore/CAAnimation.h>
#import <QuartzCore/CAMediaTimingFunction.h>

#import "AniTest.h"


@implementation AniTest

-(IBAction)ani1 // 투명도 보
{
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"opacity"];
float from = 0.1;
float to = 1.0;
ani.fromValue = [NSNumber numberWithFloat:from];
ani.toValue = [NSNumber numberWithFloat:to];
ani.duration = 1.0;
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
ani.delegate = self;
ani.autoreverses = YES;
[imgView.layer addAnimation:ani forKey:nil];
imgView.layer.opacity = 1.0;
}

-(IBAction)ani2 // 위치 보
{
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"position"];
CGPoint from = CGPointMake(50,50); 
CGPoint to = CGPointMake(250,250); 
ani.fromValue = [NSValue valueWithCGPoint:from];
ani.toValue = [NSValue valueWithCGPoint:to];
ani.duration = 1.0;
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
ani.delegate = self;
ani.autoreverses = YES;
[imgView.layer addAnimation:ani forKey:nil];
imgView.layer.position = to;
}

-(IBAction)ani3 // 크기 보간
{
CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"bounds"];
CGRect from = imgView.layer.bounds;
CGRect to = CGRectMake(20,20,120,120);
ani.fromValue = [NSValue valueWithCGRect:from];
ani.toValue = [NSValue valueWithCGRect:to];
ani.duration = 1.0;
ani.delegate = self;
ani.autoreverses = YES;
[imgView.layer addAnimation:ani forKey:nil];
}

-(IBAction)ani4 // 키프레임 커브 좌표 보간이동
{
CAKeyframeAnimation *ani = [CAKeyframeAnimation animationWithKeyPath:@"position"];
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL, 50,200);
CGPathAddCurveToPoint(thePath,NULL,
 100.0,50.0,
 220.0,350.0,
 270.0,200.0);
ani.path = thePath;
ani.calculationMode = kCAAnimationPaced;
ani.duration = 3.0;
ani.delegate = self;
[imgView.layer addAnimation:ani forKey:nil];
CFRelease(thePath);
}

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



Ninjas Going Pew-Pew!

닌자의 공격! 퓻퓻!

 Cocos2D는 아이폰 게임을 만들 때 많은 시간을 절약할 수 있게 해 주는 파워풀한 라이브러리이다. 스프라이트 지원, 멋진 그래픽펙트, 애니메이션, 물리 충돌, 사운드 엔진, 기타 등등 다양한 기능들을 지원한다.

 필자도 Cocos2D를 배우기 시작한 지 얼마 되지 않았기 때문에, 쓸만한 튜터리얼을 찾기 위해 많이 헤매고 다녔다. 하지만 정말 내가 원하는 그런 튜터리얼 - 간단하지만 애니메이션, 충돌, 오디오 등의 기능에 대한 명료한 설명이 있는 - 을 찾기가 무척 어려웠다. 마침내 스스로 간단한 게임을 만들어냈을 때, 나는 내 경험을 바탕으로 하여 다른 초보자들에게 도움이 될 만한 튜터리얼 문서를 쓰기로 마음먹었다.

 이 튜터리얼에서는 Cocos2D로 간단한 아이폰 게임을 만드는 과정을 처음부터 끝까지 훑어볼 것이다. 여러분은 튜터리얼을 그대로 따라올 수도 있고, 아니면 글 마지막에 있는 샘플 프로젝트를 바로 시험해봐도 된다. 보시다시피, 닌자 게임이 될 것이다.


Cocos2D 다운로드와 설치


 구글의 Cocos2D 코드 페이지에서 Cocos2D를 다운받을 수 있다. 이 글이 쓰여질 당시의 최신 버전은 0.99.9-final이다. 이 튜터리얼에서도 이 버전을 사용할 것이다.

 Cocos2D를 다운받았으면 템플릿을 설치해야 한다. 터미널 창을 열고 Cocos2D를 다운로드한 디렉토리로 간 뒤, 다음 명령을 입력한다.


./install_template.sh


 만약 여러분이 맥에 SDK 버전을 여러 개 설치했다든가 해서, XCode가 기본 위치가 아닌 다른 곳에 설치되었다면 명령 뒤쪽에 뭔가 파라미터를 더 붙여줘야 할 수도 있다. 


Hello, Cocos2D!


 그러면 간단한 Hello World 프로젝트로 방금 설치한 Cocos2D 템플릿을 실행해 보자. XCode를 실행하고 cocos2d-0.99.0 Application 템플릿을 골라, 새 Cocos2D 프로젝트를 만든다. 프로젝트 이름은 “Cocos2DSimpleGame” 으로 하겠다.


Cocos2D Templates


 프로젝트가 열렸으면 Build and run 을 눌러 빌드하고 실행시키자. 모두 잘 되었다면 다음처럼 시뮬레이터에 실행되는 화면이 나타날 것이다.


HelloWorld Screenshot


 Cocos2D는 게임의 레벨이나 스크린 등을 "씬"(장면)이라는 개념으로 만들어놓고 있다. 예를 들어, 하나의 게임은 크게 보아 첫 메뉴가 나오는 부분, 게임의 주된 플레이가 이루어지는 부분, 그리고 게임오버 되었을 때의 부분 등으로 나눌 수 있을 것이다. 실제 Cocos2D의 씬에서는, 여러분은 여러 개의 레이어(포토샵의 그 레이어와 비슷하다고 생각하면 된다)를 다루게 되고, 각각의 레이어는 스프라이트, 레이블, 메뉴 등등의 "노드"를 포함하게 된다. 노드는 다른 노드를 포함할 수도 있다(예를 들어 스프라이트는 그 안에 자식 스프라이트를 가질 수 있다).


 만약 여러분이 샘플 프로젝트를 보고 있다면, HelloWorldScene 이라는 1개의 씬만이 사용되고 있는 것을 볼 수 있다. 이제 우리는 바로 거기에다 게임 플레이 부분을 만들게 될 것이다. 소스를 열어 보면 씬에 "Hello World"를 보여주는 레이블을 넣는 메소드, 그리고 그 메소드를 초기화하는 코드가 있다. 우리는 이 코드를 없애 버리고, 대신에 스프라이트를 넣을 것이다.


스프라이트 넣기


 스프라이트를 넣기 전에, 당연히 스프라이트를 구성할 그림이 필요해진다. 여러분이 이 그림들을 직접 만들 수도 있지만, 나의 사랑하는 아내가 만들어준 그림들(플레이어, 표창, )을 쓸 수도 있을 것이다. 

 그림을 준비했다면, 그것들을 드래그해서 XCode의 Resources 폴더에 넣자. 그리고 “Copy items into destination group’s folder (if needed)” 부분이 체크되어 있는지를 확인하자.

 이제 그림이 준비됐다. 우리는 이제 플레이어가 위치할 곳을 생각해봐야 한다. Cocos2D에서는 왼쪽 아래 지점이 (0,0)이며 거기서부터 오른쪽 위로 갈 수록 X와 Y 좌표가 증가한다는 것을 기억하라. 우리는 이 프로젝트를 가로 모드로 할 것이므로, 가장 오른쪽 위의 좌표는 (480, 320)이 될 것이다.


 또한 우리가 오브젝트의 위치를 정할 때, 그림의 중심을 기준으로 삼는다는 것을 기억하라. 캐릭터를 스크린 왼쪽 끝에서 오른쪽 끝으로 이동시킨다고 할 때, 이동의 위치는 이 중심점을 기준으로 사용하게 될 것이다. 즉,


 - 가로 좌표를 주기 위한 가로 기준점은 [그림의 가로 크기]/2

 - 세로 좌표를 주기 위한 세로 기준점은 [그림의 세로 크기]/2


가 된다는 뜻이다.


 좀 더 이해를 쉽게 하기 위해 다음 그림을 보자.


Screen and Sprite Coordinates


 그럼 이제 실제로 만들어 보자. XCode에서 옆의 Classed 폴더를 열고 HelloWorldScene.m 파일을 선택한 뒤, 코드 편집창에서 init 메소드 부분을 다음과 같이 변경한다.


-(id) init
{  
   if( (self=[super init] )) 
   {
       CGSize winSize = [[CCDirector sharedDirector] winSize];
       CCSprite *player = [CCSprite spriteWithFile:@"Player.png"
          rect:CGRectMake(0, 0, 27, 40)];
       player.position = ccp(player.contentSize.width/2, winSize.height/2);
       [self addChild:player];
   }  
   return self;
}


 컴파일하고 실행해 보면 스프라이트가 잘 나타날 것이다. 하지만 아직 배경이 까맣게 나타나고 있다는 점도 알 수 있다. 우리의 닌자 캐릭터를 잘 보이게 하기 위해서는 아무래도 하얀 배경이 훨씬 나을 것이다. Cocos2D에서 이것을 간단하게 처리하고 싶으면 CCColoredLayer 클래스를 이용해 레이어의 배경색을 원하는 색으로 바꾸어 주면 된다. 바로 실험해보자. HelloWorldScene.h 파일을 열고 HelloWorld 인터페이스를 아래와 같이 선언한다.


@interface HelloWorld : CCColorLayer


 그 다음 HelloWorldScene.m 파일을 열고 init 메소드를 조금만 더 수정해주면 배경을 흰색으로 만들 수 있다.


if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {


 수정이 끝났으면 빌드하고 실행해 보자. 이제 스프라이트가 흰 배경에 서 있는 모습이 보일 것이다. 우리의 닌자는 이제 싸울 준비가 됐다!


Sprite Added Screenshot


목표물 움직이기

 다음으로 우리는 목표물, 즉 적들이 화면에 나타나 우리의 닌자를 공격해오도록 해야 한다. 이게 재미있게 느껴지려면 당연히 적들이 움직여야 할 것이다. 자, 그러면 적들이 화면 오른쪽에서 나타나 왼쪽으로 이동해 오도록 만들어 보자.


 다음 코드를 init 메소드 바로 앞에 넣는다.


-(void)addTarget 
{
   CCSprite *target = [CCSprite spriteWithFile:@"Target.png"
     rect:CGRectMake(0, 0, 27, 40)];

   // 적들이 어떤 Y 좌표에서 나타날 것인지를 결정한다
   CGSize winSize = [[CCDirector sharedDirector] winSize];
   int minY = target.contentSize.height/2;
   int maxY = winSize.height - target.contentSize.height/2;
   int rangeY = maxY - minY;
   int actualY = (arc4random() % rangeY) + minY;

   // 적들을 화면 오른쪽 끝에서 나타나게 만든다
   // 위쪽에서 계산한 결과에 따라 Y 좌표는 랜덤하게 나오도록 한다
   target.position = ccp(winSize.width + (target.contentSize.width/2), actualY);
   [self addChild:target];

   // 적들의 스피드를 결정한다
   int minDuration = 2.0;
   int maxDuration = 4.0;
   int rangeDuration = maxDuration - minDuration;
   int actualDuration = (arc4random() % rangeDuration) + minDuration;

   // 행동(액션)을 만든다
   id actionMove = [CCMoveTo actionWithDuration:actualDuration
     position:ccp(-target.contentSize.width/2, actualY)];
   id actionMoveDone = [CCCallFuncN actionWithTarget:self
     selector:@selector(spriteMoveFinished:)];
   [target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
}


 가능한 한 모든 명령들을 이해하기 쉽게 이름붙였다. 첫 부분은 간단한 계산으로 적들이 나타날 위치를 결정하고, 배치시키고, 그것을 플레이어 스프라이트에서 했던 것과 같은 방법으로 장면에 집어넣는 것이다.


 새롭게 들어간 부분은 액션을 넣은 부분이다. Cocos2D는 이동, 점프, 페이드(사라지거나 나타날 때) 등등, 우리가 스프라이트를 애니메이션시킬 때에 사용할 수 있는 액션을 미리 많이 준비해 놓고 있다. 우리는 여기에서 그 중의 3가지 액션을 적들에게 시킬 것이다.

  • CCMoveTo: CCMoveTo 액션은 오브젝트를 스크린 밖에서부터 왼쪽으로 곧바로 움직이게끔 한다. 전체 움직임이 다 끝날 때까지 걸리는 시간도 지정할 수 있는데, 우리는 2~4초 사이에서 랜덤하게 결정되게끔 할 것이다.
  • CCCallFuncN: CCCallFuncN 함수는 액션이 다 끝났을 때 오브젝트에게 일어나는 콜벡을 정의한다. 여기에서는 "spriteMoveFinished"라는 콜백을 정의할 것인데, 지금은 아직 그 부분을 쓰지 않았으므로 나중에 더 설명한다.
  • CCSequence: CCSequence 액션은 몇 가지 액션을 정해놓은 순서에 따라 행하게끔 만들 수 있다. 우리는 먼저 CCMoveTo 액션을 행하고, 그것이 끝나면 CCCallFuncN 액션을 행하도록 할 것이다.


 이제 위에서 설명한 콜백 함수를 넣는다. addTarget 바로 전 부분에 다음 메소드를 넣자.


-(void)spriteMoveFinished:(id)sender 
{
  CCSprite *sprite = (CCSprite *)sender;
  [self removeChild:sprite cleanup:YES];
}


 알겠지만 이 함수의 목적은 화면 밖으로 나간 스프라이트를 제거하기 위한 것이다. 이 부분은 화면에 보이지 않는 알 수 없는 스프라이트들 때문에 메모리를 낭비하지 않게 하기 위해 매우 중요하다. 물론 스프라이트의 배열을 다시 사용해서 이 문제를 해결하는 다른 좋은 방법도 있지만, 필자의 이 초보자용 트터리얼에서는 위와 같은 간단한 방법을 취할 것이다.


 적을 등장시키기 위해 마지막으로 해야 할 일이 있다.

One last thing before we go. We need to actually call the method to create targets! And to make things fun, let’s have targets continuously spawning over time. We can accomplish this in Cocos2D by scheduling a callback function to be periodically called. Once per second should do for this. So add the following call to your init method before you return:

[self schedule:@selector(gameLogic:) interval:1.0];

And then implement the callback function simply as follows:

-(void)gameLogic:(ccTime)dt {  [self addTarget];}

That’s it! So now if you compile and run the project, now you should see targets happily moving across the screen:

Targets Screenshot

Shooting Projectiles

At this point, the ninja is just begging for some action – so let’s add shooting! There are many ways we could implement shooting, but for this game we are going to make it so when the user taps the screen, it shoots a projectile from the player in the direction of the tap.

I want to use a CCMoveTo action to implement this to keep things at a beginner level, but in order to use this we have to do a little math. This is because the CCMoveTo requires us to give a destination for the projectile, but we can’t just use the touch point because the touch point represents just the direction to shoot relative to the player. We actually want to keep the bullet moving through the touch point until the bullet goes off-screen.

Here’s a picture that illustrates the matter:

Projectile Triangle

So as you can see, we have a small triangle created by the x and y offset from the origin point to the touch point. We just need to make a big triangle with the same ratio – and we know we want one of the endpoints to be off the screen.

Ok, so onto the code. First we have to enable touches on our layer. Add the following line to your init method:

self.isTouchEnabled = YES;

Since we’ve enabled touches on our layer, we will now receive callbacks on touch events. So let’s implement the ccTouchesEnded method, which is called whenever the user completes a touch, as follows:

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
 {
   // Choose one of the touches to work with
  UITouch *touch = [touches anyObject];
  CGPoint location = [touch locationInView:[touch view]];
  location = [[CCDirector sharedDirector] convertToGL:location];
   // Set up initial location of projectile
  CGSize winSize = [[CCDirector sharedDirector] winSize];
  CCSprite *projectile = [CCSprite spriteWithFile:@"Projectile.png"
     rect:CGRectMake(0, 0, 20, 20)];
  projectile.position = ccp(20, winSize.height/2);
   // Determine offset of location to projectile
  int offX = location.x - projectile.position.x;
  int offY = location.y - projectile.position.y;
   // Bail out if we are shooting down or backwards
  if (offX <= 0) return;
   // Ok to add now - we've double checked position
  [self addChild:projectile];
   // Determine where we wish to shoot the projectile to
  int realX = winSize.width + (projectile.contentSize.width/2);
  float ratio = (float) offY / (float) offX;
  int realY = (realX * ratio) + projectile.position.y;
  CGPoint realDest = ccp(realX, realY);
   // Determine the length of how far we're shooting
  int offRealX = realX - projectile.position.x;
  int offRealY = realY - projectile.position.y;
  float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
  float velocity = 480/1;
  // 480pixels/1sec
  float realMoveDuration = length/velocity;
   // Move projectile to actual endpoint
  [projectile runAction:[CCSequence actions:
    [CCMoveTo actionWithDuration:realMoveDuration position:realDest],
    [CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)],
    nil]]
}

In the first portion, we choose one of the touches to work with, get the location in the current view, then call convertToGL to convert the coordinates to our current layout. This is important to do since we are in landscape mode.

Next we load up the projectile sprite and set the initial position as usual. We then determine where we wish to move the projectile to, using the vector between the player and the touch as a guide, according to the algorithm described previously.

Note that the algorithm isn’t ideal. We’re forcing the bullet to keep moving until it reaches the offscreen X position – even if we would have gone offscreen in the Y position first! There are various ways to address this including checking for the shortest length to go offscreen, having our game logic callback check for offscreen projectiles and removing rather than using the callback method, etc. but for this beginner tutorial we’ll keep it as-is.

The last thing we have to do is determine the duration for the movement. We want the bullet to be shot at a constant rate despite the direction of the shot, so again we have to do a little math. We can figure out how far we’re moving by using the Pythagorean Theorem. Remember from geometry, that is the rule that says the length of the hypotenuse of a triangle is equal to the square root of the sum of the squares of the two sides.

Once we have the distance, we just divide that by the velocity in order to get the duration. This is because velocity = distance over time, or in other words time = distance over velocity.

The rest is setting the actions just like we did for the targets. Compile and run, and now your ninja should be able to fire away at the oncoming hordes!

Projectiles Screenshot

Collision Detection

So now we have shurikens flying everywhere – but what our ninja really wants to do is to lay some smack down. So let’s add in some code to detect when our projectiles intersect our targets.

There are various ways to solve this with Cocos2D, including using one of the included physics libraries: Box2D or Chipmunk. However to keep things simple, we are going to implement simple collision detection ourselves.

To do this, we first need to keep better track of the targets and projectiles currently in the scene. Add the following to your HelloWorldScene class declaration:

NSMutableArray *_targets;NSMutableArray *_projectiles;

And initialize the arrays in your init method:

_targets = [[NSMutableArray alloc] init];_projectiles = [[NSMutableArray alloc] init];

And while we’re thinking of it, clean up the memory in your dealloc method:

[_targets release];_targets = nil;[_projectiles release];_projectiles = nil;

Now, modify your addTarget method to add the new target to the targets array and set a tag for future use:

target.tag = 1;[_targets addObject:target];

And modify your ccTouchesEnded method to add the new projectile to the projectiles array and set a tag for future use:

projectile.tag = 2;[_projectiles addObject:projectile];

Finally, modify your spriteMoveFinished method to remove the sprite from the appropriate array based on the tag:

if (sprite.tag == 1)
{
 // target
  [_targets removeObject:sprite];
}
 else
 if (sprite.tag == 2)
{
 // projectile 
 [_projectiles removeObject:sprite];
}

Compile and run the project to make sure everything is still working OK. There should be no noticeable difference at this point, but now we have the bookkeeping we need to implement some collision detection.

Now add the following method to HelloWorldScene:

- (void)update:(ccTime)dt 
{
   NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];

   for (CCSprite *projectile in _projectiles) 
   {
      CGRect projectileRect = CGRectMake(
         projectile.position.x - (projectile.contentSize.width/2),
         projectile.position.y - (projectile.contentSize.height/2),
         projectile.contentSize.width,
         projectile.contentSize.height);

      NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];

      for (CCSprite *target in _targets) 
      {
         CGRect targetRect = CGRectMake(
            target.position.x - (target.contentSize.width/2),
            target.position.y - (target.contentSize.height/2),
            target.contentSize.width,
            target.contentSize.height);
        if (CGRectIntersectsRect(projectileRect, targetRect))
        {        
           [targetsToDelete addObject:target];
        }
      }

      for (CCSprite *target in targetsToDelete)
      {
         [_targets removeObject:target];
         [self removeChild:target cleanup:YES];
      }

      if (targetsToDelete.count > 0)
      {
         [projectilesToDelete addObject:projectile];
      }

      [targetsToDelete release];
   }

   for (CCSprite *projectile in projectilesToDelete)
   {
      [_projectiles removeObject:projectile];
      [self removeChild:projectile cleanup:YES];
   }

   [projectilesToDelete release];
}

The above should be pretty clear. We just iterate through our projectiles and targets, creating rectangles corresponding to their bounding boxes, and use CGRectIntersectsRect to check for intersections. If any are found, we remove them from the scene and from the arrays. Note that we have to add the objects to a “toDelete” array because you can’t remove an object from an array while you are iterating through it. Again, there are more optimal ways to implement this kind of thing, but I am going for the simple approach.

You just need one more thing before you’re ready to roll – schedule this method to run as often as possible by adding the following line to your init method:

[self schedule:@selector(update:)];

Give it a compile and run, and now when your projectiles intersect targets they should disappear!

Finishing Touches

We’re pretty close to having a workable (but extremely simple) game now. We just need to add some sound effects and music (since what kind of game doesn’t have sound!) and some simple game logic.

If you’ve been following my blog series on audio programming for the iPhone, you’ll be extremely pleased to hear how simple the Cocos2D developers have made it to play basic sound effects in your game.

First, drag some background music and a shooting sound effect into your resources folder. Feel free to use the cool background music I made or my awesome pew-pew sound effect, or make your own.

Then, add the following import to the top of your HelloWorldScene.m:

#import "SimpleAudioEngine.h"

In your init method, start up the background music as follows:

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"];

0.99-final update: It seems there might be a minor bug in 0.99-final where the background music will only play once (whereas it should loop) – either that or I’m doing something wrong. For a workaround, see the comments at the end of this article.

And in your ccTouchesEnded method play the sound effect as follows:

[[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew-lei.caf"];

Now, let’s create a new scene that will serve as our “You Win” or “You Lose” indicator. Click on the Classes folder and go to File\New File, and choose Objective-C class, and make sure subclass of NSObject is selected. Click Next, then type in GameOverScene as the filename, and make sure “Also create GameOverScene.h” is checked.

Then replace GameOverScene.h with the following code:

#import "cocos2d.h" 

@interface GameOverLayer : CCColorLayer 
{
  CCLabel *_label;
}

@property (nonatomic, retain) CCLabel *label;

@end 


@interface GameOverScene : CCScene 
{
  GameOverLayer *_layer;
}

@property (nonatomic, retain) GameOverLayer *layer;

@end

Then replace GameOverScene.m with the following code:

#import "GameOverScene.h"
#import "HelloWorldScene.h" 

@implementation GameOverScene

@synthesize layer = _layer; 

- (id)init 
{
   if ((self = [super init]))
   {
      self.layer = [GameOverLayer node];
      [self addChild:_layer];
   }
   return self;
}

 - (void)dealloc
{
   [_layer release];
   _layer = nil;
   [super dealloc];
} 
@end 

@implementation GameOverLayer

@synthesize label = _label; 

-(id) init
{
  if( (self=[super initWithColor:ccc4(255,255,255,255)] )) 
  {
     CGSize winSize = [[CCDirector sharedDirector] winSize];
     self.label = [CCLabel labelWithString:@"" fontName:@"Arial" fontSize:32];
     _label.color = ccc3(0,0,0);
     _label.position = ccp(winSize.width/2, winSize.height/2);
     [self addChild:_label];
     [self runAction:[CCSequence actions:
      [CCDelayTime actionWithDuration:3],
      [CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)],
      nil]];
  }
  return self;
}


- (void)gameOverDone 
{
   [[CCDirector sharedDirector] replaceScene:[HelloWorld scene]]
}


- (void)dealloc 
{
  [_label release];
  _label = nil;
  [super dealloc];
} 

@end

Note that there are two different objects here: a scene and a layer. The scene can contain any number of layers, however in this example it just has one. The layer just puts a label in the middle of the screen, and schedules a transition to occur 3 seconds in the future back to the HelloWorld scene.

Finally, let’s add some extremely basic game logic. First, let’s keep track of the projectiles the player has destroyed. Add a member variable to your HelloWorld class in HelloWorldScene.h as follows:

int _projectilesDestroyed;

Inside HelloWorldScene.m, add an import for the GameOverScene class:

#import "GameOverScene.h"

Increment the count and check for the win condition in your update method inside the targetsToDelete loop right after removeChild:target:

_projectilesDestroyed++;if (_projectilesDestroyed > 30) {  GameOverScene *gameOverScene = [GameOverScene node];  [gameOverScene.layer.label setString:@"You Win!"];  [[CCDirector sharedDirector] replaceScene:gameOverScene];}

And finally let’s make it so that if even one target gets by, you lose. Modify the spriteMoveFinished method by adding the following code inside the tag == 1 case right after removeChild:sprite:

GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];

Go ahead and give it a compile and run, and you should now have win and lose conditions and see a game over scene when appropriate!

Gimme The Code!

And that’s a wrap! Here’s the full code for the simple Cocos2D iPhone game that we developed thus far.

Where To Go From Here?

This project could be a nice basis for playing around some more with Cocos2D by adding some new features into the project. Maybe try adding in a bar chart to show how many more targets you have to destroy before you win (check out the drawPrimitivesTest sample project for examples of how to do that). Maybe add cooler death animations for when the monsters are destroyed (see ActionsTest, EffectsTest, and EffectsAdvancedTest projects for that). Maybe add more sounds, artwork, or gameplay logic just for fun. The sky’s the limit!

If you want to keep going with this tutorial series, check out How To Add A Rotating Turret to this game!

Also, if you’d like to keep learning more about Cocos2D, check out my tutorials on how to create buttons in Cocos2Dintro to Box2D, or how to create a simple Breakout game.

Feel free to chime in if you know of any better ways to do various things with this project or if there are any problems – like I said this is the first time I’ve played with Cocos2D so I have a lot left to learn!

---

출처 : http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial

* 예외처리

  - Objective-C의 예외처리를 위한 예약어
@try: Exceptions이 던져질 수 있는 block을 정의한다
@throw: Exception object를 던진다
@catch(): @try bolck안에서 던져진 exception을 catch한
@finally: @try block에서 Exceptions가 던져지던 아니던 수행되어질 block code를 정의한다

  - 모두 NSException.h에 정의되어 있다


* NSAssert

  - 특정 조건을 만족하지 않는 경우 NSInternalInconsistencyException예외를 발생시켜 프로그램을 강제로 중단시키는 매크로함수
  - 사용방법 : NSAssert(만족해야 하는 조건, 출력할 문장);


* 에러 처리

  - 에러가 발생하면 메서드나 함수의 리턴 값 형태로 에러의 발생을 리턴한다. 파일 처리에는 이러한 에러 처리를 위한 객체를 대입하도록 되어 있다. 이 경우 에러 처리에 관련된 오브젝트를 대입하면 에러가 발생해도 프로그램이 중단되지 않고 적절한 메시지를 출력하도록 할 수 있다

  - NSError : NSError.h 파일에 정의되어 있는 클래스로 에러에 대한 정보를 저장할 수 있는 클래스
* 생성자
+(id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dic

* 멤버 프로퍼티
(NSString *)domain: 에러와 관련된 기술 요소 (Cocoa, Unix…)
(NSInteger)code: 에러에 관련된 정수 값
(NSDictionary *)userInfo: 에러에 관련된 정보를 저장할 사전

* 멤버 메서드
(NSString *)localizedDescription: 에러를 설명하는 문자열로 domain과 code 출력
(NSString *)localizedFailureReason: 에러의 발생 원인
(NSString *)localizedRecoverySuggestion: 에러의 대처 방법


* XCode 화면 설정

  - [X-Code] – [Preference] 메뉴를 이용
  - [All – In – One]:  빌드, 디버깅, 검색 등이 모두 하나의 창에서 실행
  - [Condensed]: 파일 창, 편집 창, 디버깅 창 등이 모두 분리되어서 실행
  - [Default]: 그룹 및 파일 창, 편집 창 등은 통합되어 있고 디버깅 창,  GDB 콘솔 등의 일부 창은 분리된 상태에서 실행


* Break Point

  - Break Point 설정 : 원하는 지점을 선택하고 마우스 오른쪽을 클릭해서 Add BreakPoint

  - Break Point 삭제 : Break Point를 선택하고 마우스 오른쪽을 클릭해서 remove BreakPoint

  - Break Point 까지 실행 후 옵션
Continue: 코드를 계속 실행
 Step Over: 코드 다음 행으로 이동
 Step Into - 코드가 다른 메서드를 호출하는 경우 메서드 안으로 진입
 Step out - 현재 메서드를 빠져 나가서 호출하는 메서드로 이동

  - Break Point에 소리 설정하기 : 마우스 오른쪽을 클릭한 후 [Built-In BreakPoints] – [Sound out and auto-continue]

  - Break Point에 동작 지정하기
Break Point에서 오른쪽의 + 버튼을 눌러서 동작 지정 가능
[Log]: 콘솔에 로그를 남기
[Sound]: 시스템에 내장된 소리를 재생
[Shell Command]: 셀 명령 실행
[Debugger Command]: GDB 명령을 남김


* Watch Point

  - 변수의 값이 변경되는 경우를 감시하고자 하는 경우에 사용하는 기능. 이 포인트를 설정해두면 변수의 값이 변경되면 실행이 자동적으로 멈추게 된다
 
  - 디버거 창의 Variable 항목에서 선택할 수 있으며, 변수를 선택하고 마우스 오른쪽 메뉴를 눌러서 Watch Variable를 선택


* Instruments

  - 실행 중인 프로그램의 동작 상태를 감시하고 분석하는 데 사용되는 프로그램

  - 작업의 종류
1. Leaks: 메모리 누수 감지
2. Object Allocations - 객체의 생성과 해제를 감시
3. CPU Sampler – CPU 사용량 확인
4. Activity Monitor:  시스템 자원을 사용하는 상황을 감시 - 모든 프로그램 검사
5. Core Animation/OpenGL ES: 그래픽 성능 검사
6. System Usage: 프로그램의 I/O 작업을 감시


* 지역화(Localization)

  - 각 나라별로 서로 다르게 보이는 애플리케이션을 구현하고자 하는 경우 사용하는 것. 일반적으로 문자열의 변환이나 이미지의 변환 및 아이콘의 변화 등을 의미한다

  - 지역화 가능 요소: Nib 파일, 문자열, 아이콘 및 그래픽 요소, 소리 파일, Info.pList의 내용

  - 지역화 파일을 생성하는 방법 : GetInfo 메뉴에서 General – Make File Localizable 


* 문자열 지역화
 
  - 문자열을 지역화하기 위해서는 Localizable.strings 파일을 생성해서 이 파일을 먼저 지역화해준다

  - 각각의 지역화되어 있는 파일에서 “키값” = “ 실제데이터”; 로 지역화를 해주면 된다

  - 매칭되는 키 값이 없다면 있는 그대로 출력될 것이며 이를 사용할 때는 NSLocalizedString(@”키값”, @”보충설명”)형태로 사용해야 한다

  - 지역화 예제
1. Resource 디렉토리에  strings 파일을 생성(Localizable.strings) =>Mac OS X 의 Resource에 있다
2. Localizable.strings 파일을 선택하고 Info 버튼을 누르고 Make File Localizable을 누르고  Add Localization을 선택하고 Korean을 입력
3. Localizable.strings 아래의 English 파일에 입력
"Language" = "English";
"Greeting" = "Hello"; 
4. Localizable.strings 아래의 English 파일에 입력
"Language" = "한국어";
"Greeting" = "안녕하세요";


* xib 파일의 지역화

  - View-Based Application 프로젝트 생성

  - Resource 디렉토리에 그림 파일을 2개 생성

  -  ViewController.xib 파일을 선택하고 Info 아이콘을 클릭해서 General 탭에서 AddLocalization을 클릭, Korean을 추가

  - xib 아래에 2개의 언어가 보일 것이고 언어별로 다르게 디자인하면 된다


* 리소스 지역화(다른 리소스 사용)

  - Localizable.strings에 아래 문장 추가
"Image" = "Acrobat.png";
"Image" = "아크로뱃.png";

  - ViewController.h 파일에 변수 선언 및 프로퍼티 선언
@interface StringLocalizeViewController : UIViewController 
{
UILabel *label1;
UILabel *label2;
UIImageView * imgView;

}
@property(nonatomic, retain)IBOutlet UILabel *label1;
@property(nonatomic, retain)IBOutlet UILabel *label2;
@property(nonatomic, retain)IBOutlet UIImageView * imgView;
@end


* 오디오

  - 소리를 재생하는 방법은 2가지로 하나는 System Sound API를 이용하는 방법이 있고 AVAudioPlayer를 이용하는 방법이 있다

  - System Sound API를 이용하는 방법은 AIFF, WAV, CAF 파일만 재생이 가능

  - System Sound API를 이용해서 소리를 재생하기 위해서는 먼저 사운드를 시스템 소리 객체(SystemSoundID)로 만들어야 한다

  - 소리 객체 만들기: OSStatus AudioServicesCreateSystemSoundID(CFURLRef inFileURL, SystemSoundID *outSytemSoundID) 함수룰 호출해서 생성

  - 소리 재생: AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)를 호출하면 된다. 이 때 매개 변수로 kSystemSoundID_Vibrate를 대입하면 진동이 생긴다

  - 소리 객체 파기:OSStatus AudioServicesDisposeSystemSoundID(SystemSoundID *inSytemSoundID)를 호출하면 된다.
AudioToolbox.framework의 AudioToolbox.h에 메소드들이 정의되어 있음

  - 파일경로를 URL 형태로만 받기 때문에 NSString을 사용해서 선언해야 함. 멀티미디어 기능은 거의 대부분이 URL 형태로 사용하므로 반드시 알아 두어야 함

  - 아주 단순한 효과음만을 재생하는 기능이므로 단점과 제약이 많음
반복 불가능
음량 조절 불가능
재생 중에 중지 불가능
동시에 여러개 재생 불가능
확장자 제약이 심함



* AVAudioPlayer

  - 긴 소리 재생에 주로 이용하는 방법으로 아래와 SystemSoundAPI 에 비해서 아래와 같은 장점을 가지고 있습니다.
반복기능 제공
음량 조절 가능
중간에 재생을 중지 할 수 있음
동시에 여러 개의 음악을 재생할 수 있음
음악 파일의 확장자 제한이 일반적으로 없음

  -AVFoundation.framework에 존재하며 AVAudionPlayer.h 파일에 정의

  - 메소드
initWithContentsOfURL:(NSURL *)url error:(NSError **)error: 초기화
(BOOL)play : 재생
(void)pause: 중지
(void)stop: 재생 종료
BOOL Playing: 재생여부를 리턴하는 프로퍼티
NSURL *url: 소리 파일의 URL(readonly)
float volume: 소리 크기(0.0 - 1.0)
NSInteger numberOfLoops: 반복 재생 횟수
NSTimeInterval currentTime: 초기 재생 시간


* 파일 처리

  - 아이폰에서 참조하게 되는 폴더들 : Application – Documents, Library, tmp, Caches, Preferences, Application.app

  - 위의 폴더들이 존재하지만 그 중 Documents 폴더만 우리가 사용할 수 있다

  - app는 번들 디렉토리이다. 실행 중에는 이 폴더의 내용을 변경할 수 없다

  - Library 폴더 안에는 다시 Caches와 Preference 폴더가 존재하며 Preference 폴더 안에 NSUserDefaults가 생성하는 .plist 파일이 존재한다

  - Preference는 ITunes에 의해 백업된다

  - Caches는 애플리케이션에 의존적이어야 하는 파일을 저장

  - tmp는 지속적으로 저장되지 않아도 되는 파일을 저장


* 특별한 용도의 파일

  - Default.png 파일은 애플리케이션이 로드될 때 자동으로 호출되는 그림 파일이다

  - 최상위 폴더에 넣어두면 자동으로 호출되어 출력된다 (주로 로딩시에 로고를 보여주는 용도로 사용됨)

  - 이 파일은 320x480 사이즈를 권장한다

  - ~~~info.plist 파일 역시 번들 정보를 저장하고 있는 특별 용도의 파일이다

  - 이 2개의 파일은 위치와 이름이 반드시 정해진 대로 되어 있어야 하며 바꿀 수 없다


* 파일에 읽기/쓰기

  - Documents 폴더의 경로 얻기
NSArray * 배열명 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *경로명 = [배열명 objectAtIndex:0];

  - 현재 프로젝트의 번들 경로 얻기
[[NSBundle mainBundle]bundlePath];

  - NSString, NSDictionary, NSData, NSArray, NSDate의 파일로부터 읽기와 쓰기
파일로부터 읽기
-(id)initWithContentsOfFile:(NSString *)path;
URL로 부터 읽기
-(id)initWithContentsOfURL:(NSURL*)url;
파일에 쓰기
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag;
URL에 쓰기
-(BOOL)writeToFile:(NSURL *)url atomically:(BOOL)flag;


* 아카이브

  - 프로그램에서 사용되는 Object를 속성 값이나 관계를 포함에서 파일에 바이트 배열로 저장해 두는 것을 archive라고 한다

  - Object를 바이트 배열로 저장하는 것을 archive라고 하고 반대되는 작업을 unarchive라고 한 다

  - 객체를 archive하기 위해서는 NSCoding이라는 프로토콜을 따라야 한다 . 이 규칙을 따르게 되면 archive가 가능하고 NSString이나 NSDictionary 등은 이 프로토콜을 이미 적용해 두고 있다

  - NSArray나 NSDictionary 또는 NSData 등의 자료구조들은 description 메서드를 호출하면 데이터들을 propertyList로 만들어서 출력해준다. 이 때 문자열은 “”로 묶어주며 영문자와 숫자로만 구성된 경우 “”를 생략해도 된다

  - 데이터는 < >로 16진수로 묶어서 표현하게 된다

  - 저장되는 구조는 description을 확인하면 되고 파일에 저장되어 있는 경우에 읽어 올 수도 있다. 이 때 이 파일들은 XML 구조로 저장되며 수정이 불가능한 객체로 리턴되므로 주의해야 한 다


* 일반 클래스 아카이브

  - 일반 오브젝트를 아카이브 할 때는 NSCoding 프로토콜에 정의되어 있는 encodeWithCoder:와 initWithCoder: 메서드를 정의하면 된다

  - 메서드의 원형
-(void)encodeWithCoder:(NSCoder *)encoder
이 메서드의 안에서 encoder가 아카이브 할 실제 멤버들을 직접 인코딩하면 된다
-(id) initWithCoder:(NSCoder *)decoder
이 메서드의 안에서 decoder가 언아카이브 할 실제 멤버들을 직접 디코딩하면 된다

  - 기본 데이터 타입에 대한 인코드와 디코드 메서드
encodeBool:forKey   decodeBool:forKey
encodeInt:forKey  decodeInt:forKey
encodeInt32:forKey decodeInt32:forKey
encodeInt64:forKey decodeInt64:forKey
encodeFloat:forKey decodeFloat:forKey
encodeDouble:forKey decodeDouble:forKey


* CustomCell을 사용한 테이블뷰의 셀 꾸미기 예제

  - Navigation-Based Application 프로젝트 생성

  - UITableViewCell로 부터 상속받는 클래스 생성(CustomCell). Objective-C Class로 해야한다.

  - CustomCell 클래스의 헤더 파일에 변수 및 프로퍼티 선언
#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell 
{
// 레이블 2개, 이미지 뷰 1개를 가지는 셀을 만듬.
UILabel *lblName;
UILabel *lblInfo;
UIImageView *imageView;
}

// .을 이용하기 위한 프로퍼티 선언(외부와 연결할 것이므로 IBOutlet도 지정)
@property(nonatomic,retain)IBOutlet UILabel *lblName;
@property(nonatomic,retain)IBOutlet UILabel *lblInfo;
@property(nonatomic,retain)IBOutlet UIImageView *imageView;

@end

  - CustomCell 클래스의 구현 파일(.m 파일)에 synthesize 지정
@synthesize lblName;
@synthesize lblInfo;
@synthesize imageView;

  - CustomCell의 xib 파일 추가. Resources 우클릭 > Add File > User Interface > UIView

  - 추가된 CustomCell.xib 파일을 편집(인터페이스빌더). 기본으로 붙어있는 view를 제거하고 TableViewCell을 추가

  - TableViewCell의 인스펙터 창에 있는 속성 중 ClassIdentify 부분을 위에서 만든 CustomCell 클래스로 바꿔줌
 
  - CustomCell의 화면을 디자인(이미지 뷰와 레이블 등등...). 변수연결 작업(IBOutlet)

  - RootViewController.h 파일에 데이터 변수 선언
#import <UIKit/UIKit.h>

@class CustomCellAppDelegate;

@interface RootViewController : UITableViewController 
{
NSMutableArray * ar; // 테이블 뷰에 출력될 데이터 배열
CustomCellAppDelegate * app;
}

@property(nonatomic, retain) NSMutableArray * ar;

@end

  - RootViewController.m 파일에 헤더 파일 import 시키고 synthesize 지정
#import "RootViewController.h"
#import "CustomCell.h"; // 만들어진 Custom 클래스를 사용하기 위해서 import함
#import "CustomCellAppDelegate.h";

@implementation RootViewController

@synthesize ar;

  - RootViewController.m 파일의 viewDidLoad 에서 데이터 생성
-(void)viewDidLoad
{
[super viewDidLoad];
app = (CustomCellAppDelegate *) [[UIApplication sharedApplication]delegate];

// 딕셔너리는 항상 데이터(object)와 키가 함께 저장된다. 
        //아래 Name, imageName, Description 부분이 키이다. 
// 딕셔너리 내부의 데이터에 바로 접근할 수는 없고, 항상 그 딕셔너리의 키를 통해서 접근해야만 한다.
// -(id)ObjectForKey : (id)Key -> Key에 해당하는 Object 리턴
// -(NSArray *)allKeys -> 모든 key값을 리턴

NSDictionary *dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:
@"아크로뱃", @"Name", @"Acrobat.png", @"imageName", @"문서 작성", @"Description", nil];
NSDictionary *dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:
@"AIM", @"Name", @"AIM.png", @"imageName", @"기본", @"Description", nil];
NSDictionary *dic3 = [[NSDictionary alloc] initWithObjectsAndKeys:
@"앱스토어", @"Name", @"AppStore.png", @"imageName", @"앱스토어", @"Description", nil];
NSDictionary *dic4 = [[NSDictionary alloc] initWithObjectsAndKeys:
@"계산기", @"Name", @"Calculator.png", @"imageName", @"계산기", @"Description", nil];

app.ar = [[NSMutableArray alloc] initWithObjects:dic1, dic2, dic3, dic4, nil];

[dic1 release];
[dic2 release];
[dic3 release];
[dic4 release];
}

  - RootViewController.m 파일에 데이터 출력
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [app.ar count];
}

// 셀을 만들어주는 메소드. indexPath는 row와 section을 멤버로 가져서 그것으로 그룹과 행을 구분한다.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell"; // 셀을 구분하기 위한 문자열을 생성
    
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
 // CustomCell의 xib파일에 있는 내용을 객체로 만들어달라고 요청하는 부분
cell = (CustomCell *)[nib objectAtIndex:0]; // 배열의 첫번째 객체를 CustomCell * 으로 
                                                               // 변경해서 cell에 대입
}
NSDictionary *Dic = [app.ar objectAtIndex:indexPath.row]; // ar 배열로부터 행번호에 해당하는 
                                                                          // 딕셔너리를 Dic에 대입
cell.lblName.text = [Dic objectForKey:@"Name"];
cell.lblInfo.text = [Dic objectForKey:@"Description"];
cell.imageView.image = [UIImage imageNamed:[Dic objectForKey:@"imageName"]];
return cell;
}

// 행의 높이를 만들어주는 메소드
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 80;
}


* 인덱스 테이블 예제

1. Navigation Based Application 프로젝트 생성

2. UITableViewController로 부터 상속받는 SubViewController 추가

3. RootViewController.h  파일에 변수 선언 및 프로퍼티 선언
#import <UIKit/UIKit.h>

@class SubViewController;

@interface RootViewController : UITableViewController 
{
NSMutableArray * data; // 실제 데이터를 저장하고 있을 배열
SubViewController * subView; // 서브 데이터를 출력해 줄 서브 뷰 컨트롤러
}

@property(nonatomic, retain) NSMutableArray * data;
@property(nonatomic, retain)SubViewController * subView;

@end

4. RootViewController.m  파일에 synthesize이용과 loadview에서 데이터 생성
#import "RootViewController.h"
#import "SubViewController.h"

@implementation RootViewController

@synthesize data;
@synthesize subView;

- (void)loadView 
{
[super loadView];
NSArray *KIA;
NSArray *SK;
NSArray *HD;
NSArray *LG;
KIA = [NSArray arrayWithObjects:@"윤석민", @"이용규", @"이종범", @"김연아", @"이동국", @"박지성", @"차범근", @"허정무", nil];
SK = [NSArray arrayWithObjects:@"김광현", @"김재현", @"고효준", @"정근우", @"나주환", nil];
HD = [NSArray arrayWithObjects:@"김시진", nil];
LG = [NSArray arrayWithObjects:@"이택근", @"이병규", @"봉중근", nil];
NSDictionary *Dic1, *Dic2, *Dic3, *Dic4;
Dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:@"KIA", @"Team", KIA, @"data", nil];
Dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:@"SK", @"Team", SK, @"data", nil];
Dic3 = [[NSDictionary alloc] initWithObjectsAndKeys:@"HD", @"Team", HD, @"data", nil];
Dic4 = [[NSDictionary alloc] initWithObjectsAndKeys:@"LG", @"Team", LG, @"data", nil];

self.data = [[NSArray alloc] initWithObjects:Dic1, Dic2, Dic3, Dic4, nil];

[Dic1 release];
[Dic2 release];
[Dic3 release];
[Dic4 release];
self.title = @"프로야구";
}

5. RootViewController.m  파일에 데이터를 출력하고 처리하는 메소드 작성
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
    return [self.data count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) 
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    NSDictionary *Dic = [data objectAtIndex:indexPath.row];
cell.textLabel.text = [Dic objectForKey:@"Team"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if (subView == nil) 
{
SubViewController *temp= [[SubViewController alloc] initWithStyle:UITableViewStylePlain];
self.subView = temp;
[temp release];
}
self.subView.subData = [self.data objectAtIndex:indexPath.row];
[[self navigationController] pushViewController:subView animated:YES];
}

6. SubViewController.h  파일에 변수 생성과 프로퍼티 설정
#import <UIKit/UIKit.h>

@interface SubViewController : UITableViewController 
{
//루트로부터 데이터를 받을 변수
NSDictionary *subData;
//각 섹션에 해당하는 데이터
NSMutableArray *sectionData;
//인덱스 항목을 가지고 있을 변수
NSArray *index;
}

@property(nonatomic, retain)NSDictionary *subData;
@property(nonatomic, retain)NSMutableArray *sectionData;
@property(nonatomic, retain)NSArray *index;

//이름을 넘겨주면 자음을 리턴해주는 메서드
-(NSString *)subtract:(NSString *)data;
@end

7. SubViewController. m  파일에 synthesize지정과 loadView메서드 정의 
#import "SubViewController.h"

@implementation SubViewController

@synthesize subData;
@synthesize sectionData;
@synthesize index;

- (void)loadView {
[super loadView];
self.sectionData = [[NSMutableArray alloc] init];
self.index = [[NSArray alloc] initWithObjects:@"ㄱ", @"ㄴ", @"ㄷ", @"ㄹ", @"ㅁ", @"ㅂ", @"ㅅ", @"ㅇ", @"ㅈ", @"ㅊ", @"ㅋ", @"ㅌ", @"ㅍ", @"ㅎ", nil];
}

8. SubViewController. m  파일에 subtract메서드  정의
- (NSString *)subtract:(NSString*)data 
{
NSComparisonResult result = [data compare:@"나"];
if(result == NSOrderedAscending) 
return @"ㄱ";
result = [data compare:@"다"];
if(result == NSOrderedAscending) 
return @"ㄴ";
result = [data compare:@"라"];
if(result == NSOrderedAscending) 
return @"ㄷ";
result = [data compare:@"마"];
if(result == NSOrderedAscending) 
return @"ㄹ";
result = [data compare:@"바"];
if(result == NSOrderedAscending) 
return @"ㅁ";
result = [data compare:@"사"];
if(result == NSOrderedAscending) 
return @"ㅂ";
result = [data compare:@"아"];
if(result == NSOrderedAscending) 
return @"ㅅ";
result = [data compare:@"자"];
if(result == NSOrderedAscending) 
return @"ㅇ";
result = [data compare:@"차"];
if(result == NSOrderedAscending) 
return @"ㅈ";
result = [data compare:@"카"];
if(result == NSOrderedAscending) 
return @"ㅊ";
result = [data compare:@"타"];
if(result == NSOrderedAscending) 
return @"ㅋ";
result = [data compare:@"파"];
if(result == NSOrderedAscending) 
return @"ㅌ";
result = [data compare:@"하"];
if(result == NSOrderedAscending) 
return @"ㅍ";
return @"ㅎ";
}

9. SubViewController. m  파일에 viewWillAppear 메서드 재정의
- (void)viewWillAppear:(BOOL)animated 
{
[self.sectionData removeAllObjects];
NSMutableArray *temp[[self.index count]];
for(int i = 0; i < [self.index count]; i++)
{
temp[i] = [NSMutableArray arrayWithCapacity:100];
}
NSArray *name = [subData objectForKey:@"data"];
for(int i = 0; i < [self.index count]; i++)
{
NSString *pre = [self.index objectAtIndex:i];
for(int j = 0; j < [name count]; j++)
{
NSString *str = [name objectAtIndex:j];
if([pre isEqualToString:[self subtract:str]])
{
[temp[i] addObject:str];
}
}
}
for(int i = 0; i < [self.index count]; i++)
{
if([temp[i] count] != 0){
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:[self.index objectAtIndex:i], @"section_name", temp[i], @"data", nil];
[self.sectionData addObject:data];
}
}
[self.tableView reloadData];
    self.title = [self.subData objectForKey:@"Team"];
[super viewWillAppear:animated];
}

10. SubViewController. m  파일에  섹션을 만드는 메서드 재정의
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // Return the number of sections.
return [self.sectionData count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSDictionary *Dic = [self.sectionData objectAtIndex:section];
NSString *sectionName = [Dic objectForKey:@"section_name"];
return sectionName;
}

11. SubViewController. m  파일에  셀을 만드는 메서드 재정의
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    NSDictionary *Dic = [self.sectionData objectAtIndex:section];
NSMutableArray *ar = [Dic objectForKey:@"data"];
return [ar count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
    
    NSDictionary *Dic = [self.sectionData objectAtIndex:indexPath.section];
NSMutableArray *ar = [Dic objectForKey:@"data"];
cell.textLabel.text = [ar objectAtIndex:indexPath.row];
   
    return cell;
}

12. SubViewController. m  파일에  인덱스를 만드는 메서드 재정의
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return self.index;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
for(int i = 0; i < [self.sectionData count]; i++)
{
NSDictionary *Dic = [self.sectionData objectAtIndex:i];
NSString *sectionName = [Dic objectForKey:@"section_name"];
if([sectionName isEqualToString:title])
{
return i;
}
}
return -1;
}






KT앱 대회 준비하면서 모은 자료들을 정리해서 올립니다.
거의 맥부기 카페 자료들이 대부분인데 한곳에 모아서 찾아보기 쉬우라고 올려봅니다.

-푸쉬  서버  개발  관련 자료-
이지 APNS 오픈 소스 라이브러리
http://www.easyapns.com/
구글 코드 APNS 오픈 소스
http://code.google.com/p/apns-php/
서버 튜토리얼
http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/


-label이나 textView에 현재 시간을 표시하고 싶습니다-
NSDate *t = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSString *timeStr = [formatter setDateFormat:@"HH:mm:ss"];
myLabel.text = timeStr;
...
[textView scrollRangeToVisible:NSMakeRange([textView.text length]-1, 1)];


-시뮬레이터 포토 라이브러리 자신의 이미지 추가 방법-
UIImage * sShot = [UIImage imageNamed:@"imageName.jpg"];
UIImageWriteToSavedPhotosAlbum(sShot, nil, nil, nil);


-네이게이션바 스타일 바꾸기-
http://cafe.naver.com/mcbugi/1241


-이미지 자르기 함수를 소개합니다. (UIImage)-

- (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)rect
{
CGImageRef imageRef = CGImageCreateWithImageInRect([imageToCrop CGImage], rect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return cropped;
}
http://www.hive05.com/2008/11/crop-an-image-using-the-iphone-sdk/


-HTTP 라이브러리-
http://allseeing-i.com/ASIHTTPRequest/How-to-use


-json 관련-
라이브러리 http://code.google.com/p/json-framework/
json 투토리얼 http://iphonedevelopertips.com/networking/iphone-json-flickr-tutorial-part-1.html


-알럿 템플렛-
self.myAlertView = [ [UIAlertViewalloc]
initWithTitle:@"알림"
message:@"이메일을입력해주세요"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:@"확인", nil];
self.myAlertView.delegate = self;
[self.myAlertViewshow];


-푸쉬서버 구현을 위한 서버 인증서 pem 만들기-
애플 개발자 센터 apps ID 추가 (이때 와일드카드(*)는 사용하면 안됨)
키체인에서 개인 인증서 하나 만들어 애플 개발 센터에 등록
애플 개발센터에서 cert파일을 다운받아서 키체인으로 추가

키체인에서 내보내기로 p12파일로 저장

커맨드에서  p12파일을 pem파일로 변환
openssl pkcs12 -in quizers_dev_cert.p12 -out quizers_dev_cert.pem -nodes -clcerts


-전역변수를 사용해 보자...-
http://cafe.naver.com/mcbugi/55643


-JSON 2중 뎁스 이상 키 접근하기-
NSDictionary*feed =[self downloadPublicJaikuFeed];
// get the array of "stream" from the feed and cast to NSArrayNSArray*streams =(NSArray*)[feed valueForKey:@"stream"];
// loop over all the stream objects and print their titlesint ndx;
NSDictionary*stream;
for(ndx =0; ndx &lt; stream.count; ndx++){
        NSDictionary*stream =(NSDictionary*)[streams objectAtIndex:ndx];
        NSLog(@"This is the title of a stream: %@", [stream valueForKey:@"title"]); 
}


-배열 NSArray-
초기 데이터로 생성
NSArray *array = [[NSArray alloc] initWithobjects:추가할 객체들.. , nil];
 스트링으로 생성
NSArray *joins =(NSArray*)[result objectForKey:@"joins"];
길이 구하기
NSLog(@"Array size : %d " , sizeof(BUILDING_DATA) / sizeof(BUILDING_DATA[0]));


-NSString 클래스를 이용하여 문자을 넣자니 유니코드를 받아 초기화-
-(NSUInteger) UnicharLength:(const unichar*)str
{
unichar* pStr = (unichar*)str;
for( ; pStr[0] != nil ; pStr++ );
return (NSUInteger)(pStr - str);
}
[[NSString alloc] initWithCharacters:(원본문자열) length:[self UnicharLength:(원본문자열)]];


-랜덤 출력-
srandom(time(NULL));
value = random() % 100;
위처럼 하시면 0~99사이의 한수를 리턴합니다.
srandom(time(NULL)); 는 첨에 한번만 해주시면 됩니다.


-Code Sign error: Provisioning profile이 맞지 않을 때 변경 방법-
여러 장비에서 작업을 하거나 여러 프로젝트를 진행 중에 변경된 Provisioning profile이 적용되지 않아 Debug를 할 때 ”Code Sign error: Provisioning profile ‘3E6AA725-6534-46F8-B9CE-D19AC9FD854B’ can’t be found” 이런 오류가 발생하는 경우가 있는데요. 이럴 때 현재 사용중인 Provisioning Profiles로 프로젝트 세팅을 변경해주는 방법을 소개한 글(원문)이 있어서 공유합니다.

1. 실행중인 Xcode Project를 닫습니다.
2. Finder에서 프로젝트 폴더로 이동합니다.
3. 프로젝트 .xcodeproj 파일 선택하고 마우스 오르쪽 키를 눌러 '패키지 내용 보기'를 선택합니다.
4. 패키지 내용 보기를 통해 패키지 안에 있는 project.pbxproj 파일을 Xcode로 불러옵니다.
5. 검색을 통해 PROVISIONING_PROFILE 부분을 찾아 변경된 Provisioning profile 로 변경해줍니다.
6. 현재 Provisioning profile을 확인하려면 Organizer 창을 열어보면 알 수 있습니다.
7. Window > Organizer로 Organizer 창을 열고 왼쪽에 IPHONE DEVELOPMENT > Provisioning Profiles로 이동합니다.
8. 오른쪽에 있는 Profile Identifier를 복사해서 변경해주면됩니다.
9. 변경이 끝나면 project.pbxproj 저장하고 프로젝트를 열어 테스트합니다.


-아이폰 웹개발에서 디바이스 아이디 가져올수있나요?-
[[UIDevice currentDevice] uniqueIdentifier];



-Accessing Objects in a NSArray-
To access an object in an NSArray, you use the -objectAtIndex: method, as in the following example:NSArray *numbers;
NSString *string;

numbers = [NSArray arrayWithObjects: @"One", @"Two", @"Three", 
                                     nil];
string = [numbers objectAtIndex: 2];   // @"Three"

Of course, you have to be careful not to ask for an object at an index which is negative or bigger than the size of the array; if you do, an NSRangeException is raised (we'll learn more about exceptions in another tutorial).
To get the length of an array, you use the method -count, as in:
NSArray *numbers;
int i;

numbers = [NSArray arrayWithObjects: @"One", @"Two", @"Three", 
                                     nil];
i = [numbers count];   // 3


-상태바 제어-
안 보이게
[UIApplication sharedApplication].statusBarHidden = NO;

스타일
UIApplication *myApp = [UIApplication sharedApplication];
[myApp setStatusBarStyle:UIStatusBarStyleBlackOpaque];


-메모리 오버되어서 어플이 죽는 경우에 호출되는 이벤트??-

뷰컨트롤러 베이스로 작업을 한다면

- (void)didReceiveMemoryWarning

함수로 메모리가 위험할시에 위 함수를 핸들링하니 내부에 관련 대응할 처리를 구현해주면 됩니다.



-D-Day 구하기-
NSDate* date  = [NSDatedateWithNaturalLanguageString:@"2010-06-30"];
NSDate* d_day = [NSDatedateWithNaturalLanguageString:@"2010-12-31"];

NSDateComponents *dcom = [[NSCalendar currentCalendar]components: NSDayCalendarUnit
fromDate:date  
  toDate:d_day  
  options:0];

NSLog(@"day=%d",   [dcom day]);   // 184


-라디오 버튼이나 체크박스등을 찾지를 못하고  있는데-
Interface Builder 에서 library를 보시면 segmented control, switch가 보일겁니다.
말씀하시는 라디오버튼이나 체크박스는 없지만  
라디오버튼은 segmented control로 대체, 체크박스는 switch 로 대체하셔서 사용하시면 될듯합니다.


-책장 넘기기 효과-
UIView 를 하나 만들고 그 안에 UIImageView 를 만들었습니다.
이제 이미지뷰어의 내용을 채울때 책장을 넘기듯이 넘기는 방법입니다.

[UIView baginAnimations:@"stalker" context:nil]; <- stalker 는 UIView 의 이름입니다
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:stalker cache:YES];
[UIView setAnimationDuration:1.0];
imageView.image = [UIImage imageNAmed:이미지파일명];
[UIView commitAnimations];

이 걸 터치 이벤트나 이런곳에 삽입하면
책장을 넘기듯이 이미지의 전환이 일어납니다. 


-image를 fade out 효과-
[UIView beginAnimations:nil context:NULL];
[imageView setAlpha:0.0];
[UIView commitAnimations]; 


-UIView Animation 중복방지-
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveLinear]; 
....
[UIView setAnimationDelegate:self]; 
[UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
        [UIView commitAnimations];

이런 식으로 에니메이션을 만들었는데 간단하게 UIImageView를 한점에서 다른 한점으로 이동시킵니다.
근데 그래서 에니매이션이 끝나면 다시 또다른 다른 두 좌표로 해서 위의 코드가 실행되서 계속해서 UIImageView를 움직이게 하고 있습니다.

근데 질문은 1. setAnimationDidStopSelector 에서 에니매이션이 끝난것을 알기전에 강제로 에니메이션을 멈출수 있나요?
2. 제 경우에는 어떤 경우에 위 코드가 setAnimationDidStopSelector 가 호출되었을때 만 실행되는 것이 아니라 다른 부분에서도 호출하기도 합니다.  근데 문제는 동시에 위 코드가 중복되어서 호출되면 이상하게 작동해요.  그래서 꼭 위 코드를 실행(에니매이션을 commit 하기전에는 반드시 에니메이션을 강제로 멈추던지 아니면 다른 체크를 해야 할것 같은데.....  

혹시 방법이 있으면 부탁드립니다.

꾸벅~

답글 : 
[UIView setAnimationsEnabled:NO];
// 에니메이션을 종료 합니다. 


-일정시간 딜레이 후 함수 호출-
[self performSelector:@selector(playerStop) withObject:nil afterDelay :1.0f];

-(void) playerStop 
{
}


-개발 완료, 베타 테스팅용 Ad Hoc 배포-
http://cafe.naver.com/mcbugi/9042


-테이블뷰에 원격이미지를 넣을경우 스크롤이 느려지는 현상-
LazyTableImages 샘플http://developer.apple.com/iphone/library/samplecode/LazyTableImages/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009394
AsyncImageView 클래스 http://www.markj.net/iphone-asynchronous-table-image/


-테이블 뷰 섹션별로 이름 주기-
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if( section == 0 ) {
return@"발행한퀴즈";
} elseif( section == 1 ) {
return@"참여한퀴즈";
} else {
return@"진행중인퀴즈";
}
}


-정사각형으로 사진을 CROP 하고, 썸네일 크기에 맞게 리사이즈-
먼저, 출처는 다음 기사입니다.
http://tharindufit.wordpress.com/2010/04/19/how-to-create-iphone-photos-like-thumbs-in-an-iphone-app/ 
 
iPhone 사진앨범의 특징은 가로나 세로가 긴 이미지라 할지라도,
정사각형으로 사진을 CROP 하고, 썸네일 크기에 맞게 리사이즈 시킵니다.
 
위의 기사의 내용을 나름대로 보기 편하게(?) 수정을 했습니다.
 
함수명 - makeThumbnailImage
파라미터 - 원본 이미지, 리사이즈없이 CROP만 할지 여부, 리사이즈할 정사각형 한변의 길이
리턴값 - CROP 및 리사이즈된 이미지
 
- (UIImage*) makeThumbnailImage:(UIImage*)image onlyCrop:(BOOL)bOnlyCrop Size:(float)size
{
 CGRect rcCrop;
 if (image.size.width == image.size.height)
 {
  rcCrop = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
 }
 else if (image.size.width > image.size.height)
 {
  int xGap = (image.size.width - image.size.height)/2;
  rcCrop = CGRectMake(xGap, 0.0, image.size.height, image.size.height);
 }
 else
 {
  int yGap = (image.size.height - image.size.width)/2;
  rcCrop = CGRectMake(0.0, yGap, image.size.width, image.size.width);
 }
 
 CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], rcCrop);
 UIImage* cropImage = [UIImage imageWithCGImage:imageRef];
 CGImageRelease(imageRef);
 if (bOnlyCrop) return cropImage;
 
 NSData* dataCrop = UIImagePNGRepresentation(cropImage);
 UIImage* imgResize = [[UIImage alloc] initWithData:dataCrop];
 
 UIGraphicsBeginImageContext(CGSizeMake(size,size));
 [imgResize drawInRect:CGRectMake(0.0f, 0.0f, size, size)];
 UIImage* imgThumb = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 [imgResize release];
 return imgThumb;
}

위 소스를 참고하시면, 이미지를 CROP 하는 방법이나, 이미지를 RESIZE 하는 방법을 참고하실수 있을겁니다.
 
사족을 붙이자면, 왜 Resize 할지 여부를 따로 분리 시킨 이유는 실제로 사용을 해보면 Resize 루틴에서
많은 CPU 부하가 걸립니다. 그래서 UIImageView 에  contentMode를 UIViewContentModeScaleAspectFit 로 설정해서
자체적으로 리사이즈를 하게 하는 방법이 비동기적으로 괜찮습니다. (물론.. 실제 Resize된 이미지가 아니므로 메모리적인 소비는 있습니다.)
 
많은 도움 되셨으면 좋겠네요 ^^


-사진찍을때  아래에서  올라오는  메뉴 UIActionSheet-
http://ykyuen.wordpress.com/2010/04/14/iphone-uiactionsheet-example/


-uibutton disable-
http://www.iphonedevsdk.com/forum/iphone-sdk-development/2499-uibutton-disable.html


-이미지  슬라이드  샘플-
http://lievendekeyser.net/index.php?module=messagebox&action=message&msg_id=1351


-커버플로우  라이브러리-
http://apparentlogic.com/openflow/


-Xcode3.2.3과 SDK4로 업그레이드 후, 기존 앱 업그레이드 하는 법-
XCode3.2.3 과 SDK4로 버전업한 후, 기존 앱을 업그레이드 할 때 간단한 Tip 입니다.
1. XCode3.2.3과 SDK4로 업그레이드 한다. 별도로 기존 XCode 3.1 버전을 따로 보관할 필요가 없습니다.
2. 기존 앱을 새 XCode3.2.3에서 연다.
3.Group & Files를 right click -> Get Info 후
  3-1.General Tab 에서
Project Format 을 Xcode 3.2-compatible 로 바꾼다.
 3-2.Build Tab 에서
 Base SDK를 iPhone Device 4.0(배포시), 혹은 iPhone Simulator 4.0(테스트시) 로 바꾼다
 iPhone OS Deployment Target 을 iPhone OS 3.0 (즉 지원하고자 하는 하위버전으로) 로 바꾼다.
이렇게 하시면 됩니다.


-객체 타입 비교-
if ( [a isKindOfClass:b] )


-문자열 비교-
NSString *strText = idField.text;
if([srText isEqualToString:@"mihr01"])
....
else if([srText isEqualToString:@"mihr02"])
....
else
...
이렇게 하셔도 되고요 완전 같은 스트링이 아니라
 
포함된것을 찾으려면
if([strText rangeOfString:@"mihr01"].length) 


-탭뷰에 스타일시트를 붙일때-
UIActionSheet *popupQuery = [[UIActionSheetalloc]
initWithTitle:nildelegate:self
cancelButtonTitle:@"취소"
destructiveButtonTitle:nil
otherButtonTitles:@"사진찍기", @"기존의사진선택", nil];
popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque;
QuizersAppDelegate *appDelegate = (QuizersAppDelegate *)[[UIApplicationsharedApplication] delegate];
[popupQuery showInView:appDelegate.window];


-스크롤  밀어서  데이터  리플래쉬  하기-
소스코드
http://github.com/facebook/three20/tree/master/samples/TTTwitter/
설명
http://www.drobnik.com/touch/2009/12/how-to-make-a-pull-to-reload-tableview-just-like-tweetie-2/


-테이블뷰 위에 검색창 붙이는 방법-
테이블뷰 위에 검색창 넣으신 후에, viewDidLoad 메서드 부분에 [table setContentOffset:CGPointMake(0.0, 44.0) animated:NO];해주시면 처음 보여질 때는 검색창이 안 보이다가 밑으로 땡기면 나타나게 됩니다.


-네트워크  연결  됐는지  확인 Reachability-
http://www.raddonline.com/blogs/geek-journal/iphone-sdk-testing-network-reachability/
http://theeye.pe.kr/entry/how-to-check-network-connection-on-iphone-sdk



-아이폰 강제종료 버튼 이벤트-
아래 메소드가 어플이 종료될 때 수행되는 함수입니다.
종료될 때에 각종 리소스들을 Free시킬 경우에 사용됩니다.
참고하시면 될 듯 합니다~
 - (void)applicationWillTerminate:(UIApplication  *)application



-크랙 방지 클래스-
http://cafe.naver.com/mcbugi/11661



-어플을 강제 종료하는 API 는 아이폰에서 제공하지 않는다?-
http://cafe.naver.com/mcbugi/11803



-탭바 클릭시 바로 UISearchBar 클릭되도록 할려면 어떻게 해야 하나요?-
UISearchBar가 first responder가 되게 하면 됩니다.
[searchBarObj becomeFirstResponder];



-UITextField 입력값 체크하기 : 문자열 길이, 숫자여부 체크-

헤더(.h)에 UITextFieldDelegate 선언

@interface 클 래스명 : UIViewController <UITextFieldDelegate>



구현부(.m)에 다음 메쏘드를 구현하면 됨

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { 

//return NO하면 입력이 취소됨
//return YES하면 입력이 허락됨
//textField 이용해서 어느 텍스트필드인지 구분 가능

//최대길이

int maxLength = 128;

NSString *candidateString;

NSNumber *candidateNumber;


//입력 들어온 값을 담아둔다

candidateString = [textField.text stringByReplacingCharactersInRange:rangewithString:string];


if(textField == IDField) {
maxLength = 8;
} else if(textField == AgeField) {
//숫자여부 점검

//length가 0보다 클 경우만 체크
//0인 경우는 백스페이스의 경우이므로 체크하지 않아야 한다

if ([string length] > 0) {

//numberFormatter는 자주 사용할 예정이므로 아래 코드를 이용해서 생성해둬야함

//numberFormatter = [[NSNumberFormatter allocinit];

//[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];


//numberFormatter 를 이용해서 NSNumber로 변환

candidateNumber = [numberFormatter numberFromString:candidateString];


//nil이면 숫자가 아니므로 NO 리턴해서 입력취소

if(candidateNumber == nil) {

return NO;

}


//원 래 문자열과 숫자로 변환한 후의 값이 문자열 비교시 다르면

//숫자가 아닌 부분이 섞여있다는 의미임

if ([[candidateNumber stringValuecompare:candidateString] != NSOrderedSame) {

return NO;

}


maxLength = 2;

}

}

//길이 초과 점검

if ([candidateString length] > maxLength) {

return NO;

}


return YES;

}

http://cafe.naver.com/mcbugi/37651



-How to split string into substrings on iPhone?-
http://stackoverflow.com/questions/594076/how-to-split-string-into-substrings-on-iphone



-메모리 누수-
http://cafe.naver.com/mcbugi/64257


-디바이스 가로 세로 상태-
UIDeviceOrientationIsLandscape([UIDevicecurrentDevice].orientation) ?


-UITextField 에 자동 포커스 주기-
키보드 올리면서 커서를 넣을때는 아래방법을 이용하시면 됩니다.
[textField becomeFirstResponder]; 
참고로 이건 커서를 빼면서 키보드를 내리실때 사용하시면 되구요...
[textField resignFirstResponder]; 


-홈버튼이 눌렸을 때도 텍스트뷰 내용을 저장하려면 어떻게 해야할까요?-
- (void)applicationWillTerminate:(UIApplication *)application / Application Delegate 메서드 부분에 구현하시면 되지않을가요? 


-3.2 4.0  동영상  플레이-
http://iphonedevelopertips.com/video/getting-mpmovieplayercontroller-to-cooperate-with-ios4-3-2-ipad-and-earlier-versions-of-iphone-sdk.html


-한글완성형(EUC-KR)을 iPhone/Mac에서 사용할 수 있는 언어셋으로 변환하기-
http://blog.backpackholic.tv/160


-인터페이스 함수들을 편하게 가져오는 방법-
http://code.google.com/p/xcode-auto-assistant/


-#pragma mark로 코드 쉽게 구분하기-
http://cafe.naver.com/mcbugi/64408


-os4에서 applicationWillTerminate가 안먹어서 알게된것-
os4에서 applicationWillTerminate: 가 안먹어서 삽질하다가 알아낸 결과입니다.
뒷북 인지는 모르지만 혹시 모르시는 분을 위해서 적어봅니다.
os3.x 에서는 홈버튼을 한번만 누르면 applicationWillTerminate 가 아주 잘 호출됩니다.
하지만 os4 에서는 홈버튼을 한번만 누르면 
applicationDidEnterBackground 가 호출됩니다.
os4 에서 멀티태스킹을 위해서 좀 바뀌었습니다.
os4에서도 홈버튼 한번 누를때 applicationWillTerminate 가 호출되게 하려면
info.plist 에서 'Application does not run in background' 이 속성을 추가해주면 됩니다.
위 속성이 없으면 기본적으로 멀티태스킹이 되는걸로 간주합니다. (진짜 멀티태스킹은 아니지만)
위 속성이 없을때 호출되는 메소드를 실험해 봤습니다.
-----------------------------------------------------------------
처음 어플을 실행시키면
     didFinishLaunchingWithOptions, 
applicationDidBecomeActive 
이 호출되고
홈버 튼을 한번 만 누르면
applicationWillResignActive, 
applicationDidEnterBackground
호출되면서 어플이 종료되고
이상태에서 다시 어플을 실행시키면
applicationWillEnterForeground, 
applicationDidBecomeActive 
호출됩니다.
홈버튼을 두번 누르면
applicationWillResignActive
이 호출됩니다.
----------------------------------------------------------------
'Application does not run in background' 을 체크하면
홈버 튼을 한번만 누르면 applicationWillTerminate 를 호출합니다.
'근데 속성 체크 안했을때 applicationWillTerminate 는 호출이 안되는건지 궁금하네요.
아시는 분 좀 알려주세요.

답글 : 
Applicationwillterminate함수 대신에 applicationDidENterBAckground 사용하라고하네여 이곳에서 공유자원해제나 사용자데이타 저장,타이머 무효화,어플상태정보등 저장을 하라고 합니다. http://cafe.naver.com/mcbugi/65497


-COCOS2D 번개 효과-
http://www.cocos2d-iphone.org/forum/topic/370


-iPhone 4.0 호환 키보드에 버튼 or 뷰 붙이기-
기존꺼에 비해 약간 수정되 었을뿐입니다....
하지만 -_-이거 찾느라 ㅠㅠ;; 

3.1.x에서는 windows 서브뷰에 항상 키보드 뷰가 있었지만 ...
4.0 부터는 windows 에 항상 있는게 아니고, 키보드를 불렀을때 -_- 붙어서 오더라고요.. 그래서

Done 버튼 붙이는 예제 입니다. (Number 패드에)

아래 액션을 Text필드의 BeginTouched 에 연결 시킵니다.
 // 키보드가 나왔을때랑 사라질때의 이벤트를 잡아냅니다.
//3.1.X 에서는 UIKeyboardWillShowNotification 으로 잡지만
// 4.0 때문에 --; DidShow로 잡아줬습니다.
//그래야 윈도우에 키보드가 있더라고요 ;;;
-(IBAction)FieldTouched{
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardDidShowNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
    
    
}

// 키보드가 나왔을때 Done 버튼 붙여주기 
- (void)keyboardWillShow:(NSNotification *)note {  
    
    UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
    doneButton.frame = CGRectMake(0, 163, 106, 53);
    doneButton.adjustsImageWhenHighlighted = NO;
    [doneButton setImage:[UIImage imageNamed:@"DoneUp.png"] forState:UIControlStateNormal];
    [doneButton setImage:[UIImage imageNamed:@"DoneDown.png"] forState:UIControlStateHighlighted];
    [doneButton addTarget:self action:@selector(backgroundTap:) forControlEvents:UIControlEventTouchUpInside];

    //3.1.x 와 4.0 호환 키보드 붙이기
    for( UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows] ){
        for( UIView *keyboard in [keyboardWindow subviews] ){
            NSString *desc = [keyboard description];
            if( [desc hasPrefix:@"<UIKeyboard"]==YES ||
               [desc hasPrefix:@"<UIPeripheralHostView"] == YES ||
               [desc hasPrefix:@"<UISnap"] == YES )
            {
                [keyboard addSubview:doneButton];
            }
        }
    }
    
}

// 키보드가 없어질때 Done 버튼을 삭제 합니다.
- (void)keyboardWillHide:(NSNotification *)note {  
    
    for( UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows] ){
        for( UIView *keyboard in [keyboardWindow subviews] ){
            NSString *desc = [keyboard description];
            if( [desc hasPrefix:@"<UIKeyboard"]==YES ||
               [desc hasPrefix:@"<UIPeripheralHostView"] == YES ||
               [desc hasPrefix:@"<UISnap"] == YES )
            {
                for(UIView *subview in [keyboard subviews])
                {
                    [subview removeFromSuperview];
                }
                
            }
        }
    }
}

도 움 되시길 바랍니다 ;)
http://cafe.naver.com/mcbugi/62349


-배열내 숫자 값 비교해서 정렬하기-
만약에 객체내의 인스턴스를 키로 정렬할 경우에는 NSSortDescriptor 를
쓰시면 됩니다.
아래는 name으로 정렬한 예입니다.

@interface Test :
NSObject {
NSString *name;
double distance;
}
@property
(nonatomic, retain) NSString *name;
@property double
distance;
@end

@implementation Test
@synthesize name, distance;
@end


아 래는 사용방법입니다.
       Test *t1 = [[Test alloc] init];
Test *t2 = [[Test alloc] init];
Test *t3 = [[Test alloc] init];
[t1 setName:@"마바사"];
[t2 setName:@"아자차"];
[t3 setName:@"가나다"];
[t1 setDistance:1.2];
[t2 setDistance:2.5];
[t3 setDistance:0.5];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:t1];
[array addObject:t2];
[array addObject:t3];
[t1 release];
[t2 release];
[t3 release];
// 이름순으로 정렬
NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCompare:)];
[array sortUsingDescriptors:[NSArray arrayWithObjects:nameSort, nil]];
[nameSort release];
for(Test *t in array) {
NSLog(@"%@ %f", [t name], [t distance]);
}
[array removeAllObjects];


------[결 과]------
2010-07-12 17:46:13.117 Sort[5070:20b] 가나다 0.500000
2010-07-12 17:46:13.125 Sort[5070:20b] 마바사 1.200000
2010-07-12 17:46:13.130 Sort[5070:20b] 아자차 2.500000


p.s. distance로 정렬하고자 한다면 
NSSortDescriptor *distanceSort = [[NSSortDescriptor alloc] initWithKey:@"distance" ascending:YES];
nameSort 대신 distanceSort를 넣으시면 됩니다.
http://cafe.naver.com/mcbugi/65873


-[TIP] 시뮬레이터 사진앨범에 사진넣기-
1) 시뮬레이터를 실행시킵니다.

2) 맥에서 포토라이브러리에 넣을 사진을 시뮬레이터로 Drag&Drop 합니다.

3) 그러면, 사파리가 열리면서 Drag한 이미지가 표시가 될겁니다.

4) 그 표시된 이미지를 마우스로 꾸~~~~~~욱 눌러줍니다.

5) 그러면, 메뉴가 뜨면서 이미지를 저장할건지 복사할건지를 묻습니다.

6) 이미지 저장을 누릅니다.

7) 이제 시뮬레이터의 사진앨범에 가 보시면 아까 저장한 사진이 들어가있을겁니다.


---


출처 : http://cafe.naver.com/mcbugi/67700

코드가 길어질 수록 원하는 메소드를 찾아가기가 함든 경우가 많습니다.
이럴때 #pragma mark를 이용해서 코드를 쉽게 구분하고 찾아갈 수 있습니다.

 

예를 보겠습니다.

이 소스는 ADC에서 받은 Touches라는 프로젝트입니다.

 

젤 위에 세줄을 보시면  #pragma mark를 사용한걸 볼 수 있습니다.

 

Navigation Bar를 보시면 이것의 용도를 알 수 있습니다.

 

 

Navigation Bar에서 ===Animating subviews=== 라는 항목으로 구분되어서 두개의 메소드가 표시되는 걸 볼 수 있습니다.

즉 ...

#pragma mark - 는 구분용 실선

#pragma mark ===Animating subviews=== 는 항목명 출력

#pragma mark   는 그냥 빈 공간용 입니다.

 

 

좀 더 이해하기 쉽게 세줄을 주석처리 해보면

 

이렇게 몽땅 붙어서 나오게 되죠.

 

#pragma mark를 써서 코딩할때 관련된 메소드들을 잘 모아서 묶어둔다면 훨씬 보기 좋고 관리하기 좋을 것 같습니다.

전 아직 이렇게 긴 코드를 짤 수준까진 안되지만 지금부터 습관을 들여야 될 것 같네요 ^^

 

 

참고로 #pragma mark - 뒤에 공백을 넣으면 안됩니다.

 

 

공백이 들어가게 되면 위와 같이 됩니다.

그리고 Preferences... > Code Sense의 Sort list alphabetically  항목이 체크되어 있으면  Navitation Bar에서 알파벳 순으로

메소드들을 정렬해서 보여줍니다. 위의 기능을 사용하고 싶으시면 이 항목을 체크하지 마세요. 디폴트는 체크되어있지 않습니다.

 

#pragma mark가 무슨 용도인지 찾아보다가 알게되었네요.

도움이 되셨으면 합니다~ ^^


---


출처 : http://cafe.naver.com/mcbugi/64408

휴...거진 처음 시작한지 3주만에 알게 되네요... 개념도 없었다가...

 

대부분 게임 어플을 보면, 오픈지엘 100% 를 사용하고, UI 작업도 오픈지엘 에서 하고 있네요...

 

아이폰 xib UI 를 해보닌깐, 화면하나 꾸미는데 큰 어려움이 없던데...

(아직 table 주무리기 하나만 남은것 같지만...ㅋ)

 

이전 작업할때에도 마찬가지였지만, UI 작업을 코딩으로 하게되면, 창 하나 꾸미는데,

하루, 진짜 아주 열심히 하면, 창 2-3개정도 만들수 있는데, 그렇다고 UI 작업이라 아마도 인정안해 주더라구요..ㅋ

 

아이폰에서는 본 게임 부분은 Opelgl (당연히 속도 때문에...)

 

UI 관련된 부분등은 xib 띄워서, 버튼등으로 생성하고 하는게 좋더라구요.경고창이나 액션창도 그대로 사용하면 될테고...

 

창하나 꾸미는데 2-3시간 걸리던 것이, 2~30분정도로 단축되네요..^^

 

단 주의해야할 것이 있던데...

 

전 처음에 프로젝트 생성하면, opengl 쪽도 xib 로 생성해서, addsubview 로 자유자재로 이동하려고 생각했었다가,

나중에 낭패를 보았습니다 (물론 제가 실수한건지 모르겠지만...)

 

처음 프로젝트를 만들때, window 기반에 window 표면을 opengl class 로 설정하고,

그 위에 xib UI 창을 addsubview 로 하면 간편히 띄워 지더라구요..여기에, 로그인화면, 대기실 등등...꾸미면

되겠던데요...

 

그러닌깐 개념이 오픈지엘 베이스에 그 창 위에다가 xib 창을 덮어쓰고, 사용이 끝나면 그 창을 removeromsuperview

해 버리면 간단히 해결되었습니다.(다른창은 addsubview 로 다시 띄우면 되겠지요~)

 

물론 그때에도 내부 opengl 타임이 가동될것이라 예상이 되구요(아직 해보진 않았지만..ㅋㅋ)

그럴땐 상태 변수로 채크해서 잠시 멈춰서 사용하면 될테구요~~~

 

아직도 전체적인 개념은 못잡고 있지만, 그나저나, 아이폰 오픈지엘 관련서적은 어디서 구해야하는지...ㅋ

* 애니메이션의 또 다른 방법

  - CATransition : 다른 프레임워크를 사용
  - UICalloutView : 유저들이 만들어버린 것. 헤더파일 형태로 제공. 주로 풍선 도움말 같은 것을 만들 때 쓴다. 유저들이 만들었기 때문에 공식적인 애플의 SDK가 아니고, 각각의 소스마다 형태가 약간씩 다를 수 있다. 사실상 UIView에서 상속받아 만들어지는 형태가 대부분임.

* CATransition

  - Core Animation Transition의 약자임.
  - 뷰가 아니라 뷰를 담는 레이어에서 동작함
  - Quartz 코어 프레임워크에서 제공되므로 QuartzCore.framework를 링크하고, QuartzCore/QuartzCore.h 파일을 import 해주어야 함

* CATransition 타입

  - kCATransitionFade : 서서히 사라지는 효과. subtype 없음
  - kCATransitionMoveIn : 레이어가 미끄러지듯이 나타남. subtype으로 kCATransitionFrom의 방향을 지정할 수 있음
  - kCATransition

* 뷰 컨트롤러

  - 뷰 관리 모델을 담당하는 가장 기본적인 클래스
  - UITabBarController나 NavigationController도 이러한 UIViewController로 부터 상속 받아서 만들어진다
  - 프로퍼티
UIView *view: 화면에 출력될 뷰
UINavigationController *navigationController
UITaBarController *tabBarController
UIViewController *modalViewController
NSString * title     <- NavigationController에서는 제목으로 화면에 출력되며 일반적으로는 뷰의 이름입니다.

* 인터페이스 빌더를 써서 xib 파일에 만들어진 뷰(IBOutlet을 사용하는 뷰)는 awakeFromNib를 통해서 로드된다.

* 그냥 코딩으로 구현된 뷰(IBOutlet이 없는 뷰)는 loadView 메소드를 통해서 로드된다.

* 뷰 화면과 관련된 메소드

  - viewDidLoad : 뷰가 로딩된 후 호출
  - viewWillAppear:(BOOL)animated : 뷰가 화면에 나타나기 직전에 호출
  - viewDidAppear:(BOOL)animated : 뷰가 화면에 나타나고 애니메이션이 종료된 후 호출
  - viewWillDisappear:(BOOL)animated : 뷰가 화면에서 사라지기 직전에 호출
  - viewDidDisappear:(BOOL)animated : 뷰가 화면에서 사라지고 난 후 호출

* 뷰의 방향 전환과 관련된 메소드

  - (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
  - 이 메서드의 return 값이 YES이면 방향 전환을 지원하고 NO이면 지원하지 않는다
  - UIInterfaceOrientation은 회전 방향 열거형이다
  - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration : 방향 전환이 이루어지기 직전에 호출되는 메소드
  - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation : 방향 전환이 끝난 후 호출되는 메소드

* Touch 이벤트

   - UIResponder로부터 상속받는 모든 객체에서 발생하는 이벤트. 메소드 재정의만 하면 사용 가능하다
   - UITouch: 터치에 관련된 정보를 저장하기 위한 클래스로 아래와 같은 멤버를 소유하고 있다
NSTimeInterval timestamp: 터치가 발생한 시간을 저장하는 프로퍼티(readonly)
UITouchPhase phase: 터치의 상태를 저장하는 프로퍼티(readonly)로 값은 UITouchPhaseBegan, UITouchPhaseMoved, UITouchPhaseStationary, UITouchPhaseBeganEnded, UITouchPhaseCancelied
NSUInteger tapCount: 터치 한 횟수
UIView *view: 터치가 최초로 발생한 뷰
UIWindow *window: 터치가 최초로 발생한 윈도우
(CGPoint)locationView:(UIView *)view     <– view좌표계에서 터치한 위치
(CGPoint)previousLocationView:(UIView *)view      <– view좌표계에서 터치한 이전 터치의 위치 정보

   - UIEvent: 하나 이상의 터치 정보를 가지고 있는 클래스로 UITouch 객체들을 NSSet 의 형태로 저장해서 반환하며 아래와 같은 프로퍼티와 메서드 소유
NSTimeInterval timestamp: 터치가 발생한 시간을 저장하는 프로퍼티(readonly)
-(NSSet *)allTouches: 모든 UITouch 객체
-(NSSet *)touchesForWindow:(UIWindow *)window: window에서 발생한 모든 터치 객체

  - UIResponder의 터치 이벤트 메서드
- (void)touchedBegan:(NSSet *) touches withEvent:(UIEvent *)event;  <- 터치가 시작할 때 발생
- (void)touchedMoved:(NSSet *) touches withEvent:(UIEvent *)event;  <- 터치해서 이동하는 중에 발생
- (void)touchedEnded:(NSSet *) touches withEvent:(UIEvent *)event;  <- 터치를 마치면 발생
- (void)touchedCancelled:(NSSet *) touches withEvent:(UIEvent *)event;  <-터치 중 시스템 이벤트가 발생한 경우 발생

  - 멀티 터치는 객체의 MultipleTouch 속성이 YES 일 때만 가능
 
  - 멀티 터치를 지원하기 위해서는 view의 User Interaction Enabled 속성이 YES, Multiple Touch 속성이 YES 여야 한다




+ Recent posts