#if 1 // DEBUG

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

NSArray *languages = [defaults objectForKey:@"AppleLanguages"];

NSString *currentLanguage = [languages objectAtIndex:0];

NSLog(@"Current Locale: %@", [[NSLocale currentLocale] localeIdentifier]);

NSLog(@"Current language: %@", currentLanguage);

NSLog(@"Welcome Text: %@", NSLocalizedString(@"WelcomeKey", @""));

#endif


2010-12-23 06:48:34.061 Sample[327:207] Current Locale: en_US

2010-12-23 06:48:34.062 Sample[327:207] Current language: en

2010-12-23 06:48:34.063 Sample[327:207] Welcome Text: WelcomeKey


current language 의 경우 ISO639 에 따라 표기 된다.

Language

ISO 639-1

ISO 639-2

English

en

eng

French

fr

fre

German

de

ger

Japanese

ja

jpn

Hawaiian

no designator

haw

EditMode 일때는 TableView의 TableViewCell이 Touch가 안됩니다. 


즉, didSelectRowAtIndexPath: Delegate Method가 호출이 안되는것이죠. 


그런데, EditMode일때도, Touch 즉 TableView Selection이 동작했으면 하는 상황이 있다면??

이건 생각보다 쉽습니다.


UITableView Header 파일을 보면

allowsSelection 과 allowsSelectionDuringEditing이라는 옵션이 있습니다.

allowsSelection 은 기본인 Selection에 대한 옵션이고,
allowsSelection의 경우 Edit Mode에 대한 Selection입니다. 

근데, 이 녀석이 Default=NO 인 까닥에, Edit Mode에서 didSelectRowAtIndexPath: Delegate Method가 호출 되지 않은것이죠.

그냥
[tableview setAllowsSelectionDuringEdit:YES]를 한번 호출 해주면됩니다.


iPhone 사용하시다 보면, 다들 이런 경험 있으시죠? ^^

iOS App Store에서 App.을 다운로드 받을때, 20 MB 이상인 녀석들은 3G 인터넷을 통해 다운로드 받을 수 없습니다. 
App Download에 제한이 걸리게 되면, App 판매량에도 분명 영향을 미칠것입니다.

자 그럼 어떻게 이 뚱뚱한 App 녀석을 다이어트 시켜야 할까요?

최근에 알게된 것중에 하나 인데, 소개 해보려고 합니다. 
Stretchable Image 즉, 확대 가능한 이미지를 사용함으로써, Image Size를 줄이고, 궁극적으로는 Application Size를 줄일 수 있습니다.
Android개발을 해보신 분들이라면, 9 patch Image라는 녀석을 아실텐데요, 그녀석과 흡사하지만 덜 파워풀한녀석입니다 ㅎ

 

실험 대상이 될 Image는 바로 요녀석인데요, 요녀석은 40x24 입니다. 
근데 저는 이녀석을 40x24로도 사용하고, 240x24 로도 사용하고 싶습니다.

가장 간단한 방법은 이녀석을 40x24 버전과 240x24버전을 따로 준비 하는것인데, 이러면서 App.이 뚱뚱해지기 시작합니다. 

그럼 어떻게 해야 하냐 ? 범인, 아니 정답은 여기 있습니다 !

- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight;


이녀석을 이용하면, 일단 UIImage를 Stretchable하게 바꿔줍니다. 


위의 스킨 샷을 보시면, Stretchable한 Image를 이용하지 않고 그냥 UIImageView의 크기만 늘리게 되면, 
두번째 나오는 녀석처럼 쭈욱 늘어나서, 못생긴 녀석이 됩니다. 
그러나 UIImage를 Stretchable한 상태로 만들어 주면 짜잔 , 3번쨰 녀석 처럼 매끈하게 나옵니다. 


UIImage *img = [[UIImage imageNamed:@"image.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];

[stretchableView setImage:img];


예제 코드는 위와 같은데요, 
leftCap/topCap 값을 줄수 있어서, 값에 따라 Image를 upscale할때, 특정 픽셀들은 남기고, 아래 값들만 upscale시키게 됩니다. 

애석하게도, 위의 방법은 Interface Builder에서는 할 수 있는 방법이 없기 때문에, 
UIimageView에 Image를 세팅하는것을 소스코드 레벨에서 따로 해줘야 합니다. 

Apple이녀석들은 이런 괜찮은걸 만들어 놨으면, InterfaceBuilder에서도 쓸 수 있게 해줘야지 ;ㅂ; 
친구 녀석 말따라, Apple 애들도 IB를 잘 안써서 그런가 봅니다.


Sample Project 첨부해 드리니, 살펴 보실 분들은 한번씩 살펴 보시기 바랍니다. 

어떻게, 당신의 App은 다이어트에 성공하셨나요? ^^




GDB 라는 엄청나게 강력한 도구를 C언어를 배울때 부터 알고 있었고, 몇번 쓰려고 했었지만, 
Makefile + vim 을 사용하던 시절에는 그냥 귀찮아서, printf로 작업한적이 많았고, 
GDB를 그냥 Core Dump의 Back Trace용도로만 사용했었습니다. 

이번에 작업을 하면서 BreakPoint를 찍어서 GDB를 활용하려고 다시 시도 해보려고 합니다.
어쩌면 다시 귀차니즘에 printf(iOS에선 NSLog)로 돌아 갈지도 모르지만, 말이죠 ㅎㅎ

XCode에서 BreakPoint를 찍는건 상당히 간단한데요,
Code Editor의 Line Number쪽을 클릭해주면 됩니다. 

그런후에, Build & Run이 아니라 Build & Debug를 실행시켜주면, GDB와 함께 App.이 실행 됩니다. 



Build & Debug를 한후에 GDB를 켜보면 이런식으로 화면이 나오는데요

특정 BreakPoint가 어떤 순서로 찍혀 져있는지, Application Cycle도 알수 있고,
구지 NSLog등으로 찍어 주지 않아도, 

변수, 주소 값 등등을 손쉽게 Navigation할 수 있습니다. 

별로 어려운것도, 아니지만, 상당히 강력하지만 또 번거롭기도 한 녀석인데요 
이번에는 좀더 전투적으로  사용해볼까 합니다 
요새, XCode용 Plugin 을 하나 개발하고 있는데요, 

XCode는 Plugin FrameWork은 있지만, API를 공개하지 않습니다. 

Objective-C의 특성을 통해서, 공개하지 않는 Header File을 긁어 낼수가 있는데요, 
그런 신통 방통한 일을 해주는 녀석이 바로 class-dump 입니다. 

위 페이지에 들어 가시면 다운 로드 받으실수 있고요, /usr/loca/bin 이나 PATH 설정 되어 있는곳에 설치 하시면 됩니다.


class-dump는 cli command이기 때문에 Terminal 에서 사용하실수 있습니다. 

그럼 한번 마법을 부려서 XCode의 Header를 스윽 긁어 볼까요


뭐 이런식으로 나오게 됩니다. Class들이 뭐 있는지만 살펴 보려면, 


요런 녀석들이 들어 있군요 ㅎ 할 수 있는 일에 내용이 많이 없죠? 
그 이유는 바로 XCode가 Plugin 위주로 개발이 되어 있기 때문입니다. SCM , 심지어 Compiler부분까지 plugin화 되어 있죠. 

왠지 Hacker가 된것 같은 기분이 들지만, 더 낳은 개발을 위해 오늘도 달려 봅시다 ㅎㅎ



자 오늘 제가 하고 싶은 일은,  UITextField에 숫자를 입력할때, ","를 자동으로 집어 넣는것입니다.
1111->1,111 이런식으로 표기가 되는것이죠.

일단 값 변경이 일어 나는것을 Catch 하는 방법은 두가지가 있습니다.

UITextFieddDelegate의  "textField:shouldChangeCharactersInRage:replacementString:" 을 이용하는 방법이 하나이고, 

두번째로, UITextFieldTextDidChangeNotification을 이용하는 것입니다.

UIKIT_EXTERN NSString *const UITextFieldTextDidChangeNotification;


우선, Notification을 사용하는 방식은 선호 하지 않는 방법이기도 하고,  Notification을 등록하면, [textField setText:] Method가 불러 질때 에도 불러짐으로 사용하지 않고,  UITextFieldDelegate Method를 구현해서 사용하도록 하겠습니다.

TextField 의 값 Setting을 UITextField 내부에서 하지 않고, 우리 쪽 소스에서 하도록 해야 합니다.
결국 shouldChange... 샬라 샬라에서 NO 를 줘서, 변경이 일어 나지 않게 합니다.

아래 소스는 UITextField 내부 구현이 아닌, 우리의 구현으로 기본적이 동작이 가능하게 하는 코드 입니다. 

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

NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];

[textField setText:newText];

return NO;

}


여기에 newText를 우리가 원하는 포맷으로 변경해주는 Formatter를 삽입하여,  콤마를 자동입력하게 할수 있습니다.

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

NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];

[textField setText:[DataFormatter moneyFormat:newText]];

return NO;

}


iOS에서 MoneyFormatting에 관한 내용은 http://krazie99.tistory.com/49 에서 참조 하시면 됩니다 :)
이를 응용해서, iOS에서 이런저런 Auto Format Completion을 비롯한 이런저런 자동완성기능을 할 수 있을꺼라 생각됩니다.

Sample소스 첨부하니, 보시고 궁금한건 언제든 문의 하세요 ~ 




Twitter API를 구현하는건 매우 귀찮은 일이라 ㅎ
일전에 사용해본 경험이 있는 MGTwitterEngine을 사용해보고자 한다. 

내가 처음 MGTwitterEngine을 사용해본게, 아마도 1년전 즈음이었으니, 소스 구조도 빌드 방법도 많이 바뀌었구나,
내 소감부터 말하면 완전 그지 같다. 

외부 라이브러리를 가져다 쓰니까 외부 라이브러리를 빼놓은것은 이해하지만 그럼 BuildScript를 통해서
Dependency를 좀 해결할수 있게 해주던가 :(

Reference가 깨져서 빨간불 잔뜩이다. 

https://github.com/mattgemmell/MGTwitterEngine

위의 링크를 통해 MGTWitter Engine을 컴파일 해보면, 아래와 같이 에러가 잔뜩이다. 

일단 좌측에 소스가 없는것에 TouchJSON/OAuthConsumer/yaji 가 있는데, OAuthConsumer를 제외하고는 Optional Parser이기 때문에, 아예 Reference를 지워 버리자. 

OAuthConsumer는 OAuth에 필요 하기 때문에 따로 다운로드 받아서 Reference Error를 해결해주도록 하자.
다운로드 링크는 아래와 같다. 

https://github.com/ctshryock/oauthconsumer

다운로드 가 끝나고 나면 OAuthConsumer Group의 Info를 열어서 "Choose" 버튼을 눌러서,
다운로드 경로로 바꿔주자.

그런데, 여전히 빨간불 들어 오는 녀석이 있는데, 이건 MGTwitterEngine쓴애가 오타를 써서 그렇다.
Crypto 가 맞는데 Crytpo로 되어 있다. 
Crytpo Group의 Info를 바꿔서 다운받은 OAuthConsumer 소스 내의 Crypto 로 Path를 변경해주자.

그리고 여전히 한군데 빨간불이 있는데, 


그냥 일단 지워버리자 ㅋㅋ

자 그런 소스구조내의 빨간불은 다 해결했으니, 빌드 ! 그러나 현실은 에러 ㅋㅋㅋ

위의 에러는 우리가 사용하지 않을 yajl 과 TouchJSON 쪽에너 내는데, 
MATwitterEngine내의 "Twitter TouchJSON Parser" 와 "Twitter YAJLParsers" Group을 삭제 하자.


그러고 빌드 하면, 빌드 성공 !


우여 곡절끝에 빌드를 완료 했다.

빌드가 완료되었으니, 소스를 긁어다가 사용하든, 라이브러리화 해서 사용하든 하면 된다.

나는 내가 만든 소스가 아닌 경우 보통 라이브러리화 시켜서 사용하는 편인데, 
일단 귀찮고, 당장에 쓸일도 없어서,  소스 긁어다가 샘플 소스 만든 것 첨부하도록 하겠습니다. 


MGTwitterEngine개발하신분은 정말 고맙지만, 좀 이해하기 힘든 방식으로 배포 하는것 같습니다. 
이렇게 쓰기 귀찮아서야, 버전 업그레이드 된다고 해도 Major 이슈가 없다면, 업그레이드 하지 않을것 같네요.

사실 Major이슈라면, 그냥 제가 고쳐서 -_-+
다들 즐거이 개발하세요 :)

OCUnit 에서 UIImage 의 imageNamed:를 호출하면 
메인 어플과 다르게 UIImage Object를 가져 오지 못하고, nil 을 리턴하면서 프로그램이 좌자작 꼬인다. 

왜 이런 문제가 나타나는 걸까 ?

그건 바로 mainBundle이 꼬여 있기 때문이다. 
OCUnit FrameWork 단에서 mainBundle을 OCUnit Product로 변경해줬어야 할것 같은데, 그게 되어 있지 않다. 

그래서 OCUnit에서 Bundle Resource를 사용할때는 [NSBundle mainBundle]을 사용하지 않고, bundleForClass Method를 사용해서 접근하는데, UIImage imageNamed:는 내부적으로 mainBundle 을 호출하기때문에 이를 피해갈 방법이 없다. 

그렇다면 UIImage imageName:를 쓰지 말아야 하는것일까? 
그렇게 해야만 했다면, Blog에 Posting하지도 않았겠죠? ㅎㅎ

아이디어는 Objective-C의 Category 라는 특성을 이용해서 Method만 Override하는것이다.
( 사실 Method Implementation Switch 로 할려고 했는데,, 잘 안되더라고요 :( ) 

그럼 UIImage 의 imageName:을 Override할것인가 NSBundle의 mainBundle을 Override할것인가를 결정해야 하는데,

TestCode내에서 mainBundle말고, bundleForClass를 부르는것도 짜증났기에, 
NSBundle의 mainBundle 을 재정의 하기로 했다.

소스코드는 매우 간단하다.

@implementation NSBundle(OCUnit)


+(NSBundle *) mainBundle {

return [NSBundle bundleForClass:[TestClass class]];

}

@end


여기서 좀 마음에 들지 않는 부분이 있는데, 
Test하고 있는 Class를 참조 해야 한다는것이다.
<주의 ! TestClass의 Class명은 OCUnit내의 Class로 이름을 변경해주어야함 >
 
결국은 bundleForClass를 통해서 하고 있는데, 좀 다르게 깔끔하게 하는 방법을 좀 찾았으면 좋겠다.
아마도 있겠지만, 일단은 흘러가니, 나중에 라이브러리화를 고려할때, 좀더 생각해봐야 겠다.







NSObjeect 의 Header 파일을 살펴보면 + (void) load; 가 있다. 

이 녀석이 뭐하는 녀석인고 하니..

Class 가 Loading 될때, 자동으로 실행 되는 메소드다.
Java 에서의 {} 와 동일한 녀석인데, 

이걸 어떨때 많이 사용하냐면, 보통 Single-Tone Pattern을 적용할때,
sharedInstance 혹은 shard샬라 샬라 이런식으로 Interface 를 짜고, 
최초에 불러질때, Object를 Create하는 Lazy-Loading 기법을 많이 사용하는데,
종종 어플리케이션이 실행 될때 Loading되는 Static-Loading이 필요 할 때가 있다.

이때 해당 Class 에서 + (void) load를 Override하여 Static-Loading을 할 수 있다.



Android Activity 에서 Dialog띄우는 방법은 꽤 간단하며,
몇 안되는 맘에 드는 구조중 하나다 ㅋㅋ

일단, Dialog를 띄우고 싶은 시점에서 android.app.Activity에 정의 되어 있는 showDialog(int id) Method를 호출해준다.

그러면 자동으로 android.app.Activity의 onCreateDialog이 호출되는데, 
우리는 이 onCreateDialog Method를 재정의 함으로써 Dialog를 띄울수 있다.
onCreateDialog의 return type이 Dialog이고 argument로 id를 맞는다. 

즉 showDialog를 호출 해줄 때, 상황에 맞게 id 값을 넘겨주고, 그에 따라 Dialog를 적절히 만들어서,
return만 해주면 Dialog가 띄워진다는 얘기다.

@Override

protected Dialog onCreateDialog(int id) {

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setMessage("AlertDialog")

      .setCancelable(false)

      .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

          public void onClick(DialogInterface dialog, int id) {

          dialog.cancel();     

          }

      })

      .setNegativeButton("No", new DialogInterface.OnClickListener() {

          public void onClick(DialogInterface dialog, int id) {

                dialog.cancel();

          }

      });

AlertDialog alert = builder.create();

return alert;

}


위의 예제는 하나의 type의 Dialog를 띄워주는 기능을 하기때문에 argument id를 사용하지 않고, 
무조건 Dialog를 만들어서 return 해준다. 
위의 예제코드로 실행을 하면 아래와 같이 Dialog가 나타난다.

Dialog도 이런저런 Type혹은 Customizing이 가능한데, 
필요한게 있으면, 아래 android developer사이트를 참조하여 사용하면 된다. 

참고 자료 : http://developer.android.com/guide/topics/ui/dialogs.html

+ Recent posts