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
string.xml 에서 '(apostrophe)를 사용하면 에러가 발생한다.

언제나 그렇듯, 에러 문구를 잘 보면 답이 있는데, 
답은 바로 apostrophe를 사용하기 전에 '\' 를 써줘야 한다는것이다.

위의 스크린 샷 처럼 '\'를 앞에 추가 해주면 에러가 말끔히 사라진다.
아마도 몇몇 특수 문자들은 '\'를 추가 해주어야 하는것 같다.
우선, 시계를 출력하려면 당연히 Timer가 필요하다.

java.util.Timer Class 를 이용해서 일정한 주기로 Task를 실행할 수 있다.

Task는 Method를 넘겨주는게 아니라, 역시 TimerTask의 SubClassing 해서 구현한 다음 해당 Class의 Object를 넘겨줘야한다.

    @Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mCurTimeTextView = (TextView) findViewById(R.id.CurrentTimeTextView);

MainTimerTask timerTask = new MainTimerTask();

mTimer = new Timer();

mTimer.schedule(timerTask, 500, 1000);

}


Activity 가 생성될때, 불러지는 onCreate를 Override하여, TimerTask Object와 Timer Object를 만들고, timer의 schedule method를 통하여,
일정 주기로 Task가 호출 될 수 있게 한다.

2번째 Argument가 최초 실행 timing이고, 3번째 Argument가 실행 주기이다. 초단위로 시계를 보여줄것이기 때문에,
1초마다 호출되도록 프로그래밍 하였다.

private Handler mHandler = new Handler();

private Runnable mUpdateTimeTask = new Runnable() {

public void run() {

......

}

};

class MainTimerTask extends TimerTask {

public void run() {

mHandler.post(mUpdateTimeTask);

}

}


MainTimerTask Class구현부가, InnerClass 로 TimerTask를 SubClassing한 형태인데, 
Android에서는 MainThread이외에서는 UI Object를 제어 할 수 없기 때문에,
Handler를 통해서 MainThread에 Task를 넘겨 준다.

즉 시간을 Update하는 부분은 Runnable Object의 Run() Method이다.
Run() Method는 아래와 같이 구현한다.

Date rightNow = new Date();

SimpleDateFormat formatter = new SimpleDateFormat(

"hh:mm:ss dd.MM.yyyy");

String dateString = formatter.format(rightNow);

mCurTimeTextView.setText(dateString);


현재 시간을 가져오기 위해 java.util.Date Class를 사용했으며,
Date를 String으로 변환하기위해 java.text.SimpleDateFormat Class를 사용하였다.
SimpleDateFormat Class에 대한 자세한 사용법은 http://developer.android.com/reference/java/text/SimpleDateFormat.html 를 참고 하길 바란다.
formatter를 통해서 Date String을 만들고 해당 String을 TextView에 setTest로 setting함으로써 시간을 세팅할 수 있다.

이외에 유의 해야 할점은 Activity Life Cycle에 따른 Timer stop/start 이다.

onDestory/onPause 에서 Timer 를 정지 시키고, onResume에서 재 시작해주도록 아래와 같이 프로그래밍 한다.

@Override

protected void onDestroy() {

mTimer.cancel();

super.onDestroy();

}


@Override

protected void onPause() {

mTimer.cancel();

super.onPause();

}


@Override

protected void onResume() {

MainTimerTask timerTask = new MainTimerTask();

mTimer.schedule(timerTask, 500, 3000);

super.onResume();

}


간단한 프로그래밍이지만, Android Life Cycle/ MultiThread에서의 UI 제어등 알아두어야 할것이 꽤 있다.






맨날 쓰면서 자꾸만 까먹는, Path -_ㅜ

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];


이렇게 하면 SandBox내의 Documents Directory의 Path를 가져올 수 있다.


iOS SDK 에서 Email 보내기.

그 옛날, 한국에 아이폰도 없던 그 시절 ( iOS 2버전대.. ) iOS SDK 에서 Email을 보내려면 
External URL Protocol을 이용해서 Email프로그램으로 스위치 했어야 했다.
Suspending/Resume구조 도 없던 시절이라, 그야 말로 내 App은 종료되어 버렸다 ;ㅂ;

NSString *url = [NSString stringWithString: @"mailto:foo@example.com?cc=bar@example.com&subject=Greetings%20from%20Cupertino!&body=Wish%20you%20were%20here!"];
[[UIApplication sharedApplication] openURL: [NSURL URLWithString: url]];
이런식으로 프로그래밍 하였었다.

iOS 3.0 이 나오면 MFMailComposerViewController라는 녀석이 생겼는데,
UIImagePickerViewController처럼, Tunkey 방식으로 사용된다.

적절한 값만 세팅하면 내가 제어 할수 없는 ViewController가 Display되고
거기서 이메일 보내고 지지고 볶고 다 한다.

MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller
.mailComposeDelegate = self;
[controller setSubject:@"My Subject"];
[controller setMessageBody:@"Hello there." isHTML:NO];
[self presentModalViewController:controller animated:YES];
[controller release];
대강 이런식으로 하면 사용할 수 있다..

MFMailComposeViewController는 MessageUI Framework에 포함되어 있음으로,
MessageUI Framework을 추가 시키는걸 잊지 말자.
그리고 MFMailComposeViewControllerDelegate도 필요 하면 구현하면 된다!!




Inspector Window에서 Status Bar를 Unspecified로 두면 된다.

오늘은 DeskClock App에서 Alram  Event를 발생시키는 부분과 Alram BroadCast 처리하는 부분을 살펴 보려고 한다.

최초에 생각은 Process를 띄워서 Alaram BroadCast를 Generating 시킬것이라고 생각했었는데,
소스를 보고나선 역시나... 그따위로 할리가 없었다ㅋㅋㅋ

Android Package 중에 AlaramManager(android.app.AlarmManager)라는 녀석이 있어서, 그 서비스를 이용하는것이다. 

DeskClock 소스내에
com.android.deskclock.Alarams 가 실제로 AlaramManager를 이용하여, Alaram서비스를 등록한다.

    public static long addAlarm(Context context, Alarm alarm) {

        ContentValues values = createContentValues(alarm);

        Uri uri = context.getContentResolver().insert(

                Alarm.Columns.CONTENT_URI, values);

        alarm.id = (int) ContentUris.parseId(uri);


        long timeInMillis = calculateAlarm(alarm);

        if (alarm.enabled) {

            clearSnoozeIfNeeded(context, timeInMillis);

        }

        setNextAlert(context);

        return timeInMillis;

    


addAlaram() Method가 실제로 알람을 추가 하는데,
여기선 직접적으로 AlramManager도 사용하지 않고, Data Handling도 하지 않는다.
단순히, ContentResolver를 통해서 Data를 삽입하고, setNextAlert()을 통해서 다음 알람을 세팅한다.

setNextAlram()는 alram이 있냐 없냐를 봐서 없으면 Disable , 있으면 Enable시키는데, 
일단 관심은 AlaramManager를 사용하는 파트 임으로, 제끼고 
enableAlert() Method를 보도록 하자.

 private static void enableAlert(Context context, final Alarm alarm,

            final long atTimeInMillis) {

        AlarmManager am = (AlarmManager)

                context.getSystemService(Context.ALARM_SERVICE);


        if (Log.LOGV) {

            Log.v("** setAlert id " + alarm.id + " atTime " + atTimeInMillis);

        }


        Intent intent = new Intent(ALARM_ALERT_ACTION);


        Parcel out = Parcel.obtain();

        alarm.writeToParcel(out, 0);

        out.setDataPosition(0);

        intent.putExtra(ALARM_RAW_DATA, out.marshall());


        PendingIntent sender = PendingIntent.getBroadcast(

                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);


        am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);


        setStatusBarIcon(context, true);


        Calendar c = Calendar.getInstance();

        c.setTimeInMillis(atTimeInMillis);

        String timeString = formatDayAndTime(context, c);

        saveNextAlarm(context, timeString);

    }


보면 사실 별것은 없고,  AlramManager Object를 얻어오고, Intent를 만들고,
Alram에서 넘어온 시간 값 함꼐 am.set으로 넘겨 주는 이정도 이다.

실제로 사용하는건 참 간단하다고 볼 수 있다.

다음번에는 AlramManager가 trig 하는 BroadCast를 어떻게 받아서 처리 하는지 보면
기본적으로 Alaram Service 사용을 알 수 있다.

내 생각에 추가로 ContentResoulver() 쪽만 스터디 하고 나면, 
Alram App 하나정도는 금방 만들수 있지 않을까 생각한다.



XCode 3.2.5에서는 Base SDK 설정이 simulator로 설정이 안돼고, 
Device로만 선택할 수 있따.

왜 없애 버렸는지 알순 없지만... 기본적으로 "Other.."를 통해서 직접 입력할 수가 있다.

Base SDK 를 설정하면 Compiler 옵션으로 SDKROOT=iphoneosx.x 나 SDKROOT=iphonesimulatorx.x이런식으로 들어 간다.
Other를 선택해서 iphoneosx.x 이런식으로 값을 따로 설정 하면 된다.

위와 같이 iphonesimulator라고 써주면, Base SDK를 Simulator로 설정할 수 있다.

iphonesimulator라고만 쓰면 최신의 sdk 이고,
특정버전이 필요 한 경우에는 iphonesimulator4.2 이런식으로 기술 해주면 된다.

iOS 에서 외부 라이브러리를 사용할때,
가끔 가끔 unrecognized selector에러가 날때가 있다.

이건 참 짜증나는 경운데, 보통은 Objective-C Runtime버그로 이러는 경우가 있다.

라이브러리를 구현한쪽에서 Category를 이용해서 Class를 확장해서 사용하는 경우, 
Category에 정의된 selector를 Objective-C Runtime에서 못찾는 게 되는 경우다. 

Objective-C Runtime에서 라이브러리 Loading을 안한 경우인데,
이때는 Compiler , Other Linker Flag에서 -all_load 혹은 -force_load를 해줌으로써  해결할 수 있다.

Compiler/Runtime은 보통 믿고 사용하는 녀석들인데,
98% 만 믿어야지, 100% 믿으면 안된다 ;ㅂ;


+ Recent posts