스토리보드가 도입되면서 스토리보드상에서 콘트롤러와 콘트롤러를 연결해주기만 하면 전면전환이 되는 편리한 기능이 추가 되었다.

(모달,푸쉬 그리고 애니메이션 옵션등 약간의 설정이 필요하긴 하다.)


예전에 코드로 전부 써주어야 했던것이 스토리보드로 흡수되면서 기본적인 화면전환의 경우는 코드없이도 간단하게 구현이 가능하다.

물론 몇번의 마우스클릭의 수고는 필요.


만약 Modal, Push 형태 이외의 화면전환이 필요한 경우는 화면전환 전용클래스(UIStoryboardSegue)를 이용하여 정의(커스텀)해 줄수 있는데

화면전환에 관련된 코드가 세그에 정의됨으로써 보다 가독성도 높일 수 있다.


스토리보드 사용법에 대해서는 다른 블로그에도 많이 소개 되어 있으므로 세그에(Segue)를 커스텀하여 애니메애션을 주는 방법을 몇가지 소개햐려 한다.



우선 UIStoryboardSegue를 상속받는 세그에(Segue)클래스를 생성한다.


@interface KJCodeMenuSegue : UIStoryboardSegue


@end




정의에는 perform메소드를 재정의 해준다. 여기에서 애니메이션과 화면전환을 정의 해준다.



- Push, Pop 세그에 를 커스텀 할 경우 (modal의 경우도 마찬가지 방법으로 처리해도 된다.)


- (void)perform {

    

//하나의 클래스에 여러개의 처리를 위해 id 이용하여 분기해도 .

NSLog(@"%@", self.identifier);

//뷰콘트롤러 인스턴스 취득

    UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;

    UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;

    

    if (self.identifier isEqualToString:@"menuPush") {

        

        //화면전환 애니메이션

        [UIView transitionWithView:sourceViewController.navigationController.view

                          duration:0.5f

                           options:UIViewAnimationOptionTransitionCrossDissolve

                        animations:^{

                            //실제로 화면전환코드를 작성. 애니메이션은 겹치지 않도록 NO 설정

                            [sourceViewController.navigationController pushViewController:destinationViewController animated:NO];

                        }

                        completion:nil];

        

    }else if(self.identifier isEqualToString:@"menuPush") {

        

        //화면전환 애니메이션

        [UIView transitionWithView:sourceViewController.navigationController.view

                          duration:0.5f

                           options:UIViewAnimationOptionTransitionCrossDissolve

                        animations:^{

                            //실제로 화면전환코드를 작성. 애니메이션은 겹치지 않도록 NO 설정

                            [sourceViewController.navigationController popViewControllerAnimated:NO];

                        }

                        completion:nil];

        

    }

    

}



- 컨테이너뷰에서의 임베디드 세그에(embedSegue)를 커스텀할 경우


- (void) perform {

    

    NSLog(@"%@", self.identifier);

    

    UIViewController *containerViewController = (UIViewController *) self.sourceViewController;

    UIViewController *nextViewController = (UIViewController *) self.destinationViewController;

    UIViewController *currentViewController = (UIViewController *) containerViewController.currentViewController;

    

    //컨테이너 콘트롤러에 화면전환 뷰콘트롤러를 자식뷰콘트롤러로 추가한다.

    [containerViewController addChildViewController:nextViewController];

    // 현재뷰콘트롤러에 이동(삭제) 라는것을 통지한다.

    [currentViewController willMoveToParentViewController:nil];

    

    // 전환될 뷰콘트롤러와 현재 뷰콘트롤러의 크기를 조정한다.

    nextViewController.view.frame = currentViewController.view.frame;

    

    // 화면전환 애니메이션 정의한다.

    [containerViewController transitionFromViewController:currentViewController

                                         toViewController:nextViewController

                                                 duration:0.1f

                                                  options:UIViewAnimationOptionTransitionCrossDissolve

                                               animations:^{

                                                   

                                               }

                                               completion:^(BOOL finished) {

                                                   //컨터이너뷰 안에서의 화면전환처리를 정의한다.

                                                   containerViewController.currentViewController = nextViewController;

                                                   [currentViewController removeFromParentViewController];

                                                   [nextViewController didMoveToParentViewController:containerViewController];

                                               }];

    

}



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

iOS6부터 새로 추가된 AutoLayout 기능은 너무 편리하다. (사실... iOS6에서는 인터페이스가 엉망이라 불편했는데 iOS7부터는 많이 개선되었다.)


본론으로 들어가서, 스토리보드에AutoLayout을 적용하면 동적으로 프레임 좌표변경이 불가능하다.

정확히 말하면 분명 메모리상에서는 이미 바뀌어 있지만 화면에 표시될땐 변경된 좌표로 표시되지 않는 현상이 발생한다.

그 뿐만아니라 layer에 회전을 시키는 것도 제대로 동작하지 않는다.


지금까지 해결방법으로 스토리보드에서 AutoLayout를 해제하여 해결해왔는데 (정확히 말하면 AutoLayout사용을 포기한거임) 이제서야 제대로 된 해결방법을 알아냈다.



그 방법이란 좌표를 변경할 뷰를 일단 한번 띄어냈다가 다시붙이는 방법이다. 그렇게 하면 그부분만 AutoLayout의 쓸데없는 방해를 피할 수 있다.


예를들어..


for (UIView *v in moveView.subviews) {

        

        if ([v isKindOfClass:[UIView class]] == YES) {

            //변경을 원하지 않으면 패스

            continue;

        }

        

        [v removeFromSuperview];

        

        //프레임을 변경

        v.frame = CGRectMake(0, 0, 100, 200);

        

        [self.view addSubview:v];

        

    }



대충 이런식의 코딩이 가능하다.

참고로 뷰를 띄었다가 붙였을경우 IBOutlet에 연결된 커넥션이 무효화될까 걱정했었는데 무사히 동작한다.


혹시 모를 AutoLayout 때문에 고생하시는 개발자분들은 참고하시길.


2013.03.22 추가내용.

AutoLayout적용한 컨트롤러에서 UIScollView(스크롤뷰)에서 컨텐츠사이즈가 적용안되는문제가 발생하는데 아래와 같은 방법으로 해결할 수있다.


- (void)viewDidLayoutSubviews {

    

    [super viewDidLayoutSubviews];

    

    // 스코롤 컨텐츠사이즈 변경

    [scView setContentSize:CGSizeMake((_rView.frame.size.width + _tView.frame.size.width),

                                      [UIScreen mainScreen].bounds.size.height)];

    

    [self.view layoutSubviews]; //이부분이 중요

}



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

개인적으로 iOS개발할때 불편하다고 생각되는것 중 하나가 CoreData를 이용한 개발이다.

간단한 테이블뷰어정도는 제공해줘도 될듯한데 XCode에 포함시키지 않았을까? 

애플의 사소한 배려가 없는 상황에서 지금까지는 그때끄때 무식하게 코딩으로 직접 만든 나만의 프로그램으로 해결하곤 했다. 

뭐 테이블뷰로 대충 붙여놓은 간단한 프로그램이긴 하지만.. 귀찮은 일이긴 했지만 자주 사용했으므로 나름 유용한 방법이긴했다. 

물론 앱스토어에 유용한 툴도 있겠지만 거의 파견형태로 일하는 나로서는 내 맥북도 아닌데 아무 어플리케이션이나 깔수 없었다.

그것이 무식한 방식을 선택한 이유이기도 했다.


그런데....무식하면 몸이 고생한다고... 정말 간단한 SQL만으로도 검색할 수 있는 방법이 있었다. 

sqlite명령어를 쓰면 되는데 왜 그생각을 지금까지 못했을까..


어쨋거나 반성은 나중에 하고 일단 메모해 둔다.


1. 당연한 얘기지만 확인해야할 파일을 작업폴더로 복사한 후 터미널을 실행시켜 해당 폴더로 이동한다.

2.sqlite를 실행한다. sqlite>라는 커서가 나타나면 성공.

sqlite3




3. 사용할 데이타베이스를 지정한다. (혹시 실행경로와 파일경로가 다를경우 풀패스를 적어주면 된다.)

sqlite3 데이타베이스 파일명




4. 종료할 경우 (명령어의 경우는 .(점) 으로 시작한다는 것에 주의)

sqlite>.exit 또는 .quit




5. SQL명령어는 ;를 마지막에 붙여준다.

*실제로 XCode에서 정의한 테이블명, 컬럼명 앞에 Z가 붙어 있다.

select * from 테이블명;




6. 검색시 컬럼명을 같이 출력하고 싶을때에는 헤더출력설정을 ON으로 해준다.

sqlite> .headers ON



7. 줄맞춤이 필요한 경우

sqlite> .explain ON



8. 기타 필요한 명령어

#데이타베이스 접속확인

sqlite> .database

#전체 테이블명 확인

sqlite> .tables


물론 도음말은 

sqlite> .help





Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

iOS5부터인가 나왔던 기능으로 알고 있는데 block코딩과 더불어 생소한 문법 중 한놈이다.

굳이 써야되는 필요성을 느끼지 못한놈인지라 넘겨두고 있었는데 최근 비동기처리가 필요한 플젝을 하면서

어쩔수 없이 조금 공부하게 되었다.

정말 간단하게 구현되고 편리하므로 아마도 자주 써줘야겠다.

어쨌든 내가 아는한도내에서 두가지 정도 메모해 둔다.


첫번째 제일 자주 쓰는 구문. 이건 싱글톤 처리할때 쓰는 패턴이다. 다른방법도 있지만(인스턴스가 nil인지 아닌지 비교해서 분기해주는...)

어쩐지 이게 더 멋있어 보인다. 

쓰는 방법은 dispatch_once_t 라는 변수를 선언해주고 dispatch_once라는 메소드에 초기화 처리를 해주면 메소드 이름처럼 

딱 한번만 실행한다. 그러니 결과적으로 싱글톤이 됨.


+ (AVAudioPlayerManager*)sharedAVAudioPlayerManager {

    static dispatch_once_t pred = 0;

    __strong static id _vrObject = nil;

    dispatch_once(&pred, ^{

        _vrObject = [[self alloc] init];

    });

    return _vrObject;

}


두번째로 비동기를 위해 스레드 처리할 경우. 예를들어 메인스레드에서 시간이 걸리는 처리를 해버리면 

당연히 화면이 멈추는 현상이 나타난다. 왜냐면 화면처리는 메인스레드에서 처리되므로.. 

바꾸어 말하면 시간 걸리는건 메인스레드에서 처리하지 않는것이다. 다만 화면상태를 바꾸고 싶을땐 메인스레드에서 처리

해야 하므로 그 부분만 메인스레드로 넘기는 것이 포인트.


1.일반큐와 메인큐를 각각 선언한다. 

2.스레드를 사용하기 위해 dispatch_async 라는 메소드를 선언하고 일반큐를 파라메터로 넣는다.

3. 다음 처리로직안에 화면제어를 할 필요가 있는 처리에 대해선 메인큐(메인스레드)로 넘겨준다.


정말 간단하다. 백문이불여일견! 직접 소스를 보자.


dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_async(globalQueue, ^{

    

        //시간이 걸리는 처리를 한다고 치자.

        int count = 0, i;

        for(i = 0; i < 10000; i++){

            NSLog(@"count = %d", count);

            count++;

        }

        

        //진행상태를 화면에 표시해주기 위해 메인스레드에 화면변경로직을 코딩.

        dispatch_async(mainQueue, ^{

            [self updateProcress:[self logStringWithCount:count]];

        });

        

        //또 시간이 걸리는 처리가 있다고 한다면...

        for(i = 0; i < 10000; i++){

            NSLog(@"count = %d", count);

            count++;

        }


        //끝났을 경우의 처리를 해준다. 여기서도 메인스레드로 처리해줌.

        dispatch_async(mainQueue, ^{

            [self updateProcress:[self logStringWithCount:count]];

            [_indicatorView stopAnimating];

            _startButton.hidden = NO;

        });

    });


Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

인류문명의 발달은 잉여노동력 그리고 적절한 환경에 의한 결과라는 제레미다이아몬드라는 유명한 아저씨의 주장이 요 며칠간 가깝게 다가왔다.

지금 마치 내상황이 잉여노동력(할일없음) + 적절한 환경(프로젝트 끝나감) 이므로 저아저씨 말대로라면 뭔라도 해야 될 상황이다.


프로젝트는 슬슬 끝나가고 할일은 없고.. 인터넷 서핑도 하루이틀이지.. 

잉여력이 넘치고 넘치던 찰나 결국 남는 잉여력을 소스코드를 이쁘게 고치는데 사용키로 했다.


지금까지 딜리게이트을 남발하면서 막개발을 해 왔는데 이번기회에 iOS5부터인가 지원되지 시작한 blocks비동기처리 방식를 써볼까? 라는 생각이 든것이다.


이제까지 문법자체가 생소해서 쓰고 싶지 않았는데 시계바늘을 본드에 붙여놓은듯 시간이 가질 않으니 어쩔수 없다.

결과적으론 익숙해지는데 조금 시간이 걸렸으나 한번 적용해보니 코드량이 상당히 줄어드는걸 체감했다.

더이상 딜리게이트를 남발하지 않으리...


그럼 일단 block방식으로 코딩하기 위한 문법부터 살펴보자.


블럭구문 기본적인 선언방법

^(파라메터,...) {

    //실행로직

};

 

블럭구문을 대입할 경우의 코딩

반환값 (^변수명)(파라메터,...) = ^(임시 파라메터...) {

    //실행로직

};


여기까지만 보면 어떻게 하라는 얘기인지 모를것이다. 직접 짜보자.


블럭구문으로 test라는 함수작성

int (^test)(int) = ^(int x) {

    return x + x;

};

 

// 결과출력

NSLog(@"%d", test(10));



블럭구문을 typedef로 정의할땐 이렇게 한다. 일반적으로 사용하는 패턴이므로 숙지해 두자.

typedef 반환값 (^변수명)(파라메터,...) 


블럭구문내에 변수사용하기.

__block를 변수앞에 붙이면 블럭구문내에서 값변경이 가능하다.

물론 붙이지 않아도 참조는 가능.


__block int x = 300;

void (^print)(void) = ^(void) {

    x++;

    NSLog(@"%d", x);

};


자.. 여기까지는 기본적인 사항이고 실제로 하고싶은 블럭구문을 메소드에 넘겨주는 걸 해보자.

- (void)example:(double (^)(int, int))func {

int i = 3;

int j = 4;


if(func(i,j) > 5) {

  //처리1...

}else {

  //처리2...

}

}


이런식으로 쓴다. 파라메터로 받은 블럭구분 결과로 처리가 가능하다. 이전엔 이와같은 처리를 딜리게이트로 해결했었다.


실제 호출될 경우에 블록구문내 처리를 하도록 코딩하자.

[self example:^(int n1, int n2) {

//실행처리

    return i+j;

}];


자 여기서 아까 언급했던 typedef 를 상기해보자.

그렇다 메소드에 넘기는 블럭구문을 간단히 정의하여 메소드길이를 줄일수 있다.


//이렇게 정의한 다음..

typedef double (^calcHandler)(int a, int b);



//이렇게 바꾸어 쓸수 있다.

- (void)example:(calcHandler)func {

...

}


딜리게이트의 경우는 프로토콜 정의해주고 그걸 받는쪽에서 위임받아서 처리해주는 구문까지 해야하므로 상당히 코딩량이 많아지는데 반해서 블럭은 상당히 코드를 줄어든다.


조금 설명이 부족한거 같아 한가지 예를 더 들어보자. 

어떤 특정 메소드를 실행이 끝난뒤 후처리를 해주는 메소드를 만들어보자.

블록구문이 사용된 우리가 흔히보는 메소드이다.

[self.navigationController dismissViewControllerAnimated:(BOOL) completion:^{

        //TODO: 

        //화면이 닫힌후 후처리.

    }];

    


먼저 블럭구문부터 관찰해보면 반환값, 파라메터도 없는 블럭이다.

^{ 

//TODO: 

}


위와 같은 처리를 하는 calcTime라는 이름의 메소드를 만들어보자. 그리고 이 메소드가 실행이 완료되면 실행되는 블럭을 만들어 보자.


우선 메소드 선언.

- (void)calcTime:(void ^(void)handler;


그리고 메소드 정의

- (void)calcTime:(void ^(void)handler {


//무쟈게 오래걸리는 처리가 있다고 치자..

//...

//처리가 다 끝난부분에서 블럭구문을 실행하게 한다.

handler();

}

    

    

실제 호출하여 사용할때에는..

[self calcTime:^{

//이메소드 끝날때 처리할 내용 코딩..

}];



써놓고 보니 별 내용없네.. 

테스트하면서 코딩한게 아니라서 소스에 오류가 있을수 있으므로 참고하시길..


Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

최근에 갑느님이 알아먹기 쉽게 하기위한 목적으로 데모용 프로젝트를 만들어야 했다.

아무리 데모용이지만 어느정도 형태는 갖추어야 하기에..여기저기 이미지를 다른앱에서 긁어모아 

만들기 시작했다.

지금 프로젝트엔 디자이너님이 없는 관계로..


아무튼 가져온 이미지를 문제없이 잘 사용하고 있었는데(적어도 시뮬레이터에는...) 정작 디바이스에 넣어보니 

콘솔에 지저분한 에러로그를 토해내며 XCode가 반항을 하기 시작했다.

「While reading ... pngcrush caught libpng error」


분명 컴파일 에러이면서 앱은 실행되는 그런 이중적 태도를 취하면서 위에 로그대로 해당이미지는 표시하지 않는 짜증나는 현상이었다.

혹시 다른앱에서 퍼온 이미지라서 그러나? 뭔가 락이 걸려있는 이미지인가? 뒤가 구린생각에 오만가지 생각을 다봤지만

그런건 들어본적이 없기에 구글링+스택플로우 에서 찾아보기로 했다.


두시간정도 삽질하니 아래의 두가지정도의 패턴이 발견되었다.


첫번째..

해당파일이 프로젝트 리소스 파일 타겟에서 빠져있는경우.

정말 이경우라면 문제는 간단히 해결.그러나 내가 이런 초보적인 실수를 할리가 없어라고 자신없게 외치며 확인을 해보니 이문제는 아니었음.

참고로 확인방법은 해당파일을 선택한 후 오른쪽 프로파티창에 Target Membership 에서 해당 타겟을 

체크해 되어 있는지 확인하면 됨. 안되 있음 해당 타겟에 체크해면 해결.

 



그리고 두번째..

인터레이스(インタレース)가 적용된 이미지파일일 경우.


인터레이스가 존재자체가 몰랐기에 구글링 해보니 이미지 저장방식의 한종류라고 함.

간단히 예를들어 웹페이지를 열었을때 이미지가 처음에는 뿌옇게 보이다가 다운로드가 진행되면서 

점점 선명해지는 이미지.. 바로 그런이미지로 저장하는 방식이란다.


어쨋든 인터레이스로 되어 있는 이미지는 XCode에서 받아들이지 못하니 변경해주야 한다.

물론 포토샾으로 해주면 깔끔하겠지만 회사에서 준 맥에는 당연하게도 포토샾이 없고 그렇다고 gimp는 사용할 줄 모르고..

더군다나 데모용이었기 때문에 맥 기본 이미지뷰어에서 대충 해결하기로 했다.


해당파일을 이미지뷰어에서 연 후 내보내기에서 알파값속성체크를 제거후 저장한다.




근데 신기한건.. 다른 앱에서 가저온 이미지인데 왜 안된걸까? 

Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

프로젝트를 마치고 납품할때 설계서를 납품하기도 하지만 종종 고객에 따라 Doxyzen형태의 문서를 요구 하는 경우가 있다.

예전 자바프로젝트의 경우 이클립스에서 Doxyzen용으로 자동으로 코멘트를 생성하주는 편리한 기능이 있었는데 (물론 템플릿 자동생성성)이 기능을 XCode에서도 사용할 수 있다.

상훈아 고마워~ 


이 방법을 몰라 이전 프로젝트는 코멘트를 다 손코딩으로 했었는데 무식하면 몸이고생한다.


방법은 간단. 아래의 설명을 따라하길 바란다.


1.일단 아래링크에서 파일을 하나 다운받는다.

http://www.duckrowing.com/2011/05/14/using-the-doxygen-helper-in-xcode-4/

download the service. <-- 여기에서 다운



2.다운받은 파일은 .tar 로 압축되어 있는데 압축을 풀면 workflow라는 확장자의 파일이 나타난다.

이렇게 쇠파이프를 들고 있는 로봇이다.



3. 자 그러면 이 로봇을 더블클릭하여 실행한다.

* 실행하면 동시에 이 로봇이 말도 없이 사라지는데 놀라진 말자. 중요! 나중에 등록시 이놈의 이름(Document Code)이 필요하므로 외워두자. 


4. 시스템 환경설정에서 키보드를 선택하고 왼쪽에 어플리케이션을 선택하 "+" 버튼을 눌러 아래의 그림과 같이 등록한다.

여기서 등록할때 이름을 3번에서 외워두었던 Document Code를 넣어준다.

단축키를 등록하게 되는데 내경우는 cmd+shift+D 로 지정하였다.


5. 여기까지 되었으면 XCode로 돌아가보자.

XCode가 실행중 이었다면 종료후 다시 시작해야 적용이 된다.


메소드를 선택한 후 아까 지정했던 단축키를 누르면 아래와 같이 Doxyzen용 템플릿이 나타난다.







Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

최근의 앱들은 대부분 서버와의 연동이 필요하게 되었다. 서버의 API를 이용함으로 인해서 발생되는 여러가지 에러를 NSError카테고리를 만들어 관리하면 편하다.

그로인해 서비스쪽(API를 이용한 비지니스 로직)과 화면쪽이 명확하게 분리되는 장점도 있다.


NSError+Service.h


#import <Foundation/Foundation.h>


#define KJErrorDomain @"com.kj-code.KJError"


/**

 * @brief enum KJErrorエラー定義

 */

typedef enum {

    KJErrorCodeNone = 0,                    ///< エラーなし

    KJErrorCodeInternalError,               ///< 内部エラー

    KJErrorCodeAuthenticationError,         ///< 認証エラー

    KJErrorCodeResponseStatusError,         ///< 応答エラー

KJErrorCode;


/**

 * @brief KJCodeエラーカテゴリークラス

 */

@interface NSError (Service)


/**

 * @brief NSErrorインスタンスを返却します。

 * 

 * @param KJErrorCode 定義したエラーコード

 * @param NSString エラーメッセージ文字列

 *

 * @return NSError インスタンス

 */

+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code localizedDescription:(NSString*)localizedDescription;


/**

 * @brief NSErrorインスタンスを返却します。

 * @details KJErrorCodeに定義されているエラーメッセージがセットされます。

 *

 * @param KJErrorCode 定義したエラーコード

 *

 * @return NSError インスタンス

 */

+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code;


@end




NSError+Service.m


#import "NSError+Service.h"


static NSString* _KJ_CODEDESCRIPTION[] = {

    @"",

    @"KJError.InternalError",

    @"KJError.AuthenticationError",

    @"KJError.ResponseStatusError",

};


@implementation NSError (Service)


+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code localizedDescription:(NSString*)localizedDescription {


    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:localizedDescription forKey:NSLocalizedDescriptionKey];

    

    NSError *error = [[self class] errorWithDomain:KJErrorDomain code:code userInfo:userInfo];

    return error;

}


+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code {


    NSString *description = _KJ_CODEDESCRIPTION[code];

    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:description forKey:NSLocalizedDescriptionKey];

    

    NSError *error = [[self class] errorWithDomain:KJErrorDomain code:code userInfo:userInfo];

    return error;

}




사용할 때는  이렇게 사용한다.



NSError *error = [NSError errorWithKJErrorCode:KJErrorCodeInternalError

                                                      localizedDescription:message];



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

XCode로 로 개발중에 C++로 개발된 모듈을 추가해야 할 일이 생겼다.

어차피 Objective-C도 C언어에서부터 나온거라 그냥 추가해주면 될줄 알았지만 당연하게도XCode는  에러를 뱉어내면서 저항을 했다.

나중에 이런작업을 또 할까... 없을거라 생각되지만 혹시나 해서 잊어버리기전에 블로그에 적어놓자.

인터넷을 찾아본 결과 몇가지 설정이 필요하고 XCode에 필요한 규칙이 있었다.


먼저 샘플 cpp 파일을 만들어보자. 파일추가에서 아래와같이 선택하면 간단히 템플릿이 만들어진다.



추가된 파일에 cpp형식으로 샘플코드를 넣어보자.


Sample.h 와 Sample.cpp파일            

 


자..이제 실제로 뷰 콘트롤러에서 불러보자.

참고로 cpp파일을 이용하기 위해서는 ViewController.mm와 같이 확장자를 mm 으로 변경 해 주어야 한다.



Hello 함수가 정상적으로 잘 호출되었다.



그러나.. 실제 프로젝트에 적용하니 또다시 XCode에서 반항하며 알수없는 에러들을 내뱉었다.

인터넷으로 여기저기 알아본 결과 스택플로우에서 해결방법 발견!

http://stackoverflow.com/questions/6913637/xcode-4-2-c0x-libc-cannot-find-iostream


읽어보니 이유는 모르겠지만 설정을 두군데 정도 손으 봐야 한다.

Build Settings에서 C++ Standrad Library 를 libstdc++로 변경 그리고 Compile Sources As 를 Objective-C++로 바꿔준다.



어쨋든 성공은 했으니 좋다.



Posted by 악당잰 트랙백 0 : 댓글 2

댓글을 달아 주세요

  1. addr | edit/del | reply BlogIcon 곱게자란녀석 2015.01.11 17:16 신고

    안녕하세요. 같은 문제에 봉착하였는데.. 전 해결이 안되서.. 여쭤보려고 문의드렸습니다.
    전 c 파일과 cpp 파일을 전부 사용해야 하는 경우인데요.. Apple Mach 0 linker error 를 자꾸 내뱉습니다.
    cpp로 작성된 함수명들에 대해 에러를 내뿜고 있구요.
    댓글엔 그림을 넣을 수 없어 제가 다른곳에 질문글 올렸던 링크도 같이 드립니다...
    http://www.clien.net/cs2/bbs/board.php?bo_table=cm_app&wr_id=12324

    • addr | edit/del BlogIcon 악당잰 2015.01.11 19:07 신고

      도움이 될지는 모르겠으나 제가 아는 한도내에서 답변을 드리자면..

      C, C++로 만들어진 정적 라이브러리를 이용하시나요?
      그렇다면 arm64를 적용하지 않은채 빌드된 라이브러리에선 캡쳐와 같은 에러가 발생한 경험이 있습니다.
      라이브러리를 arm64용으로 빌드해서 달라고 하면 됩니다.

      c, cpp 파일을 직접 프로젝트에 넣어서 사용하신다면 캡쳐에러만 가지고는 알수가 없네요.
      다만, objective-c 에서는 기본적으로 c++은 그냥그대로 사용 못하는걸루 알고 있구요. c의 경우도 구조체 같은 경우 ARC를 먹지 않기 때문에
      결국은 오브젝티브C용으로 랩핑을 해줘야 합니다.

      도움이 되었는지 모르겠네요..

자초지종을 얘기하자면 회사 현장에서 예전사람이 작업을 했던 iPhone소스를 압축 파일로 넘겨받았다.

이런식으로 소스를 준 이유야 물론 소스관리서버(SVN 또는 Git)를 이용하질 않았기 때문일거다.

뭐 환경이 그러니 로컬에서 수정하는 식의 작업을 하게 됬는데...

워낙 사양의 변경이 잦아 다시 뒤로 돌리는 일이 많아지고 그래서 로컬에서만이라도 소스이력을 관리해야할 필요가 생겼다.


다행히도 XCode에는 로컬Git를 이용할수 있으므로 그걸 사용하려고 했지만.., 이런식으로..



 

이메뉴는 프로젝트 처음 실행시에만 나오므로 나의경우는 이미 프로젝트가 생성된것이니 메뉴에서는 불가능..


인터넷을 좀 검색했지만 좀처럼 답을 찾을수 없었는데 다시생각해보니 Git를 사용해본 사람이라면 간단히 해결될 문제였다.

메뉴에서 선택하여 Git를 만들지 못하니 그냥 커맨드로 하면 되는거다.


1. 터미널을 열고 소스폴더로 이동한다.

2. git init 명령을 실행하여 Git생성

3. git add . 을 실행하여 폴더에 모든 파일을 추가(옆에 점을 꼭 찍어야함)

4. git commit -a -m "init commit" 을 실행하여 커밋

5. XCode를 실행하여 프로젝트를 열면 Git을 사용할수 있다. 오거나이져에도 자동으로 보임.


결국 Git에 대한 나의 무지로 삽질을 좀 했는데 Git랑 조금이나마 가까워진것에 의미를 두자.



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요