Lomohome.com :: 괴발자 모근원


안녕하세요 모근원입니다.


2011년. 그러니까 4년전에 iOS5 와 맞추어 새로 등장한 알림센터에 일정을 표시해주던 어플 '일정목록' 을 만들어서 앱스토어에 올렸었습니다.


http://lomohome.com/382



그리고 약 1년후에 애플에 의하여 강제 삭제.. 를 당했었는데요. (알림센터에 위젯이란게 없던시절.. 알림센터에 노티를 이용하여 정보를 남기는것은 사용법 위반)


http://lomohome.com/394


한동안 업무도 바쁘고 이 앱은 쳐다도 보지 못하다가.. 최근에 애플와치를 구입하게 되었습니다.


애플와치에 일정표시하는방법이 맘에 들지않아 iOS9 (Watch os2) 출시 이전에 애플와치용 일정목록 앱을 만들게되면서 


첫 작업으로, 예전 일정목록 앱을 위젯으로 복각 해서 오늘 심사가 통과되었습니다.


3년만의 업데이트라.. 예전앱을 기억하실지 모르겟지만.. 사용법이야 똑같구요.


현재 개발이 거진 완성된 다음버전은 iOS9 (Watch os2)로 나올 예정입니다. 그리고 애플워치 지원과 함께 Tier1 유료 (0.99 아 한국은.. 1.09인가요) 로 판매해볼 계획이니


무료인 지금 다운받으셔서 좋은리뷰 하나씩 남겨주시면 감사하겠습니다 ^^;











앱스토어 링크 : 

https://itunes.apple.com/kr/app/events-widget-events-on-today/id470828213?l=ko&ls=1&mt=8 (한국스토어)

https://itunes.apple.com/us/app/events-widget-events-on-today/id470828213?l=us&ls=1&mt=8 (미국스토어)











** 일부 사용자들에게서 아이폰과 워치가 앱이 연동이 안되는 버그가 있습니다. 애플의 watch os 2 버그로 보입니다 ㅠ 다음을 시도해주세요.


1. 일정위젯을 아이폰에서 삭제하고 아이폰과 애플와치를 모두 껏다 켭니다.

2. 일정위젯을 다시 앱스토어에 받으시고 애플워치에도 설치해주세요. 

3. 먼저 일정위젯을 아이폰에서 실행합니다. 애플워치에선 아직 실행하지마세요.

4. 아이폰에서 캘린더, 연락처 접근권한을 물어보게 되면 허락해주시구요 

5. 그다음 워치앱을 실행시킨다음 2~3분을 기다려주세요.

6. 표시할 일정이 없다고 나오다가 일정이 표시되기 시작하면 아이폰 일정위젯 앱에서 워치부분 설정을 변경하여 입맛에 맞게 설정을 변경합니다.

Posted by 모근원

- 애플의 UIReferenceLibrary API의 Standalone 사전 정책에 맞지않아 어플이 내려갔습니다 ㅠ

  그간의 성원에 감사드립니다. 흑



창조경제의 일환으로 앱스토어에 개인개발자 자격으로 올리는 마지막(?)이 될지도 모르는 앱이 등록되었습니다.

운이 좋은건지 나쁜건지 토요일에 앱스토어에 올라오고 바로 사업자등록과 통신판매업을 요구를 하네요... ㅠ

설명 나갑니다.. ㅠ


 


InDic - 간단하고 빠른 사전 어플











iOS7 으로 업데이트되면서 한영 내장사전이 추가 되었습니다.
내장사전을 쓰는 사전어플이 없나 봤더니 QuicDic 이란 앱이 있었습니다. 그런데 영어키보드만 입력이 가능하도록 해놨더라구요..
그래서 한글도 입력이 가능한 간단한 사전앱을 만들어서 지금 앱스토어에 올라가있습니다.


유료버전에는 앞으로 다양한 기능추가를 할 예정입니다.
유료나 무료나 사전 검색하는데에는 차이점이 없습니다.
유료버전에는 광고 라이브러리가 포함되지않아 용량도 더 적고, 빠르고, 가볍습니다.
무료로 전환했습니다.
원하시는 기능이나 제안사항 있으시면 리플이나 메일 주세요~ 최대한 반영하도록 노력하겠습니다.
감사합니다~



- 다운로드 : https://itunes.apple.com/kr/app/indic-gandanhago-ppaleun-sajeon/id723205033?mt=8



* 애플의 API 사용 misuse 로 어플이 내려갔습니다. 그간 성원에 감사드립니다.

Posted by 모근원

지금은 앱스토어에서 내려간 비운의 앱 일정목록 ㅠ

노티바에 일정을 올려서 편하게 일정을 확인할수 있는 앱!

자세한 설명은 이곳으로


이제는 앱스토어엔 등록되진않지만 혹시 컴파일하여 폰에 깔아두고 쓰실분이나 (저는 이렇게 쓰고있습니다 -_-) 공부하실 목적으로 소스를 보고 싶으신 분들은 (도움이 되려나요;;) 다운받으셔서 맘껏 유린해주세요 -_-; 받아가시면 리플도 하나씩 달아주시구요 ㅠ









소스 다운받기 -> 

EventList.zip


* 소스보시고 욕하지 마시고;;; 소스엔 매우많은 버그와 허접함이 숨어있을수있습니다.

* 소스의 수정/재배포는 허용하지 않습니다. 

블로그의 링크를 걸어주세요.



Posted by 모근원
Day:
예쁘고 간편한 디데이 알리미

아이콘 태생부터 우여곡절이 많았던 디데이 앱입니다 ㅠ
오늘 새벽에 앱스토어에 Ready for Sale 되어
블로그에 글 남겨 봅니다 ㅎ


개발자 등록하고 첫 어플인 '일정 목록' 을 만들게 된것은 개인적으로 필요해서 만들어 쓰다가 
너무 꼼수를 많이 부려 정식 앱스토어는 못올라갈것 같았습니다.
그래서 그냥 한번 올려나 봤는데 우연히 얻어 걸려 등록 된것이고..

두번째 앱스토어 등록 앱인 Day: 도 여자친구와 기념일을 카운트 하다가 
다른 D-Day 앱들중 내가 필요한 기능만 뽑아서 간편히 만들어볼 생각으로 만들게 된 앱입니다.

어플 개발 초반부터 아이콘에 대한 고민을 많이 했는데
무조건 심플하고 이쁜 아이콘을 만들고 싶어 초안을 만들어 몇몇 친한 친구들에게 평가를 부탁했지만
 

결과는 참담했습니다. 다들 반응이 너무 촌스럽다 복잡하다 뭐하는건지 모르겠다.. ㅎㅎ

그래서 수정에 수정에 수정을 겪은 뒤에 심플한 최종 아이콘이 나오고 (컨셉은 D - day 입니다 ㅎㅎ)
어플도 친구들의 의견을 최대한 반영해서 약 2주정도 작업을 했습니다.

일본어 버전 번역은 친한친구 박동안님께서 수고해주셨구요 ㅎ


영어는 제가 대충 번역기 돌려 했는데.. 조만간 미국인친구에게 부탁해야겠네요 ㅎ 


그리고 애플에 심사를 올렸는데…

리젝하고 올리고 리젝하고 올리고 리젝당하고 올리고 리젝당하고 올리고 리젝당하고 올리고 ㅠㅠ
어줍잖은 어플 심사해준 사과님께 감사 ㅋ
 
리젝 사유가… 3.4 설치된 어플이름과 iTunes 어플이름이 다르다고…

iTunes에 등록된 어플 이름은 D-Day 였는데
설치되면 Day 만 표시된다고 리젝당하고…

그래서 어플이름을 Day : 로 통일한다음 다시 올렸더니
어플 이름 뒤에 설명이 들어가있어서 키워드로 빼라고 다시 리젝..

그당시 제출한 어플 이름이
Day : Simple and Beautiful D-Day reminder - 예쁘고 간편한 디데이 알리미
였는데 뒤에 한글 설명이 문제가 되었습니다.

한글 사용자들 검색때문에 넣어둔것이었는데 
이전 앱은 잘 통과되더니.. 리뷰어 제대로 된통 걸렸다 싶었습니다.

그래서 그부분을 다시 키워드로 빼놓고
다시 올리니 이틀만에 승인이 났네요 ㅎ 

일단 어플 자체는 굉장히 심플하지만 개발하면서 새로운 부분 공부한것도 많고
새로운 기술을 적용한 부분도 많아서 알아주신다면 감사하겠습니다 ^^; 
기술적인 부분보다도 예쁜 어플 화면에 집중한것도 있구요.. 폰트라던지.. 
아직 버그도 많고 추가해야겠다고 예정만 하고 아직 들어있지 않은 기능도 많지만
많이 다운받아 주시고 격려의 한말씀 부탁드릴게요 ㅎㅎ

Posted by 모근원

현재 iTunes App Store 에서 삭제 당했습니다.
그간 성원에 감사드리오며 더 좋은 앱으로 보답드리겠습니다 ㅠ


iOS5 부터 추가된 노티센터에 앞으로의 일정을 쭉 표시해주는 앱을 만들어서 몇달간 앱스토어에 올렸었는데요.

 

이번에 애플직원이 심심했는지 레디포세일중에 제 앱을 가지고 딴지를 걸었네요..

 

처음엔 노티센터에 일정을 등록하는데 non-public APIs 를 쓴것이 아니냐 그래서...

 

I would like to talk to you about your app submission. Specifically, the use of the Notification Center.
 

 
장문의 이메일을 보냈는데.. non-public API는 한개도 쓴것이 없고 노티센터에 local-push notification 을 이용하여
 
메세지 등록을 시키고 있다 라고 했더니..
 
오늘 답변이 온것이..
 
This is not an intended use of the Notification Center and the following needs to be removed for the app to remain for sale.
 
 
1) The app places the events from iCal into the notification center to be displayed until the event happens. This is a notification center misuse (2.5)
2) App contains a shortcut to the Settings app in the Information section (2.5)... change was not verified in ver. 3.11
3) When the scheduling is complete, the app presents a Quit button (10.1)
 
1.노티센터에 앞으로의 일정을 표시 시키는것이 노티센터를 지들이 정한 용도에 맞지않게 쓰는것이며 (misuse),
2.iOS 5.1에서 제거된 셋팅숏컷이 남아있고 (이건 없앴는데! 이상하게도 ㅠ)
3.노티센터에 일정등록이 완료되면 종료버튼 (exit(0)) 이 있어서 가이드라인 위반이라고 하네여...
exit(0)은 종료 메세지 띄운뒤에 나오면 되는줄알았더니만..
 
2번 3번은 어찌 수정하면 될것 같지만.. 노티센터에 일정을 보이게 하는게 가이드라인 위반이라면
이 앱의 존재자체가 필요없기 때문에..
한번더 메일을 보내봐야 알겠지만 곧 앱스토어에서 내려가게 될것 같습니다.
 
혹시 이런 자질구레한 앱이지만 필요하신분들은 내려가기전에 어서 받으셔서 사용하시면 될것 같습니다.
허접스런 앱이지만 오늘 기준으로 5만여분이 받아주셔서 쓰시고 유용하다는 리뷰도 많이 받았었는데 ㅠㅠ
 
안타깝네요 ㅠ 흑
 
*내려가기전에 iTunes 에서 IPA 파일을 백업받아두시면 계속 쓰실수 있습니다 ㅠ 흑





일정목록 (Events List)

 비밀리에 -_- 개발이 진행되었던 일정관리 어플이 드디어
앱스토어에 출시 되었습니다.

아래는 클리앙에 올린 소개글 전문.
개발비화도 살짝... -_- 

 

일단 제가 만들었고. 버그도 많고 거지같습니다만;;;

 

개발자 등록하고 난생 처음 리뷰를 통과한 어플이라 (엉엉 ㅠㅠ) 한번 소개글 올려봅니다.

 

iOS5 에 알림센터가 생겼는데요

 

알림센터를 주욱 내려서 향후 일정을 표시하면 어떨까.. 싶어서 iOS5 beta 1 때부터 조금씩 만들었었는데요

 

beta 4인가 5부터... 알림센터에 [캘린더] 로 일정이 나오더라구요 -_-;;;;;;;;;;;;;;;; 원래 없었는데!!!

 

그래도 강점이라면 [캘린더] 위젯은 오늘, 내일 일정만 나오는데 제 어플은 기간 관계없이 향후 일정을 표시합니다!

 

근데 단점이 더 많아요.... 알림센터 위젯을 어플에서 접근가능한 API 가 없어서... (그렇게 알림센터좀 같이 쓰자고 애플에 숱한 메일을 보내봤지만 ㅠㅠ)

 

일정이 추가 삭제 될때마다 다시 어플을 실행시켜서 업데이트 시켜주셔야 합니다-_-;;;;

 

완전 꼼수 어플이에요. 로컬 노티를 막 보내서 알림센터에 줄창 떠있게 하는.... 리젝당할줄 알았는데 통과되데요;;;

 

아... 이렇게 쓰니까 춫천app 카테고리보다 잡담에 들어가야할것 같네요 ㅠ

 

더 유용한 어플을 만들수 있게 저에게 힘을 주세요~~ :-)

 

아... 어플 링크를 빼먹었네요.

 

꽁짜니까 부담없이 받으셨다가 지워주세요 ㅎㅎ

 

- 미국 스토아

http://itunes.apple.com/us/app/events-list/id470828213?l=ko&ls=1&mt=8

 

- 한국 스토아

http://itunes.apple.com/kr/app/events-list/id470828213?l=ko&ls=1&mt=8

 

* former 를 future 로 바꾼 버전 2 도 지금 심사중입니다! 확인하실겸 버젼 2 도 받아주세요. (나중에~) 


Posted by 모근원

영등포가는 길 중에 구로역에서.

신도림 디큐브시티 공사현장

영등포 타임스퀘어



책읽는 소영. 요즘 부쩍 인터리어에 관심을..


친구들 만나러 가는 길

커피기다리는 상민이 

당구집 아들경력 동안이


자넷 쏘?

오랜친구 동안이와 상민이

당구만 15년을 쳐도 실력은 그바닥들 ㅋ


귀여운(?) 상민

파마한 동안이




Sony NEX-5 / E18-55mm F3.5-5.6 OSS / RAW / Lightroom 3 / 2011. 1. 29

'내가찍은사진 > 디카사진들' 카테고리의 다른 글

로또  (0) 2011.02.27
짱어  (0) 2011.02.27
우리집 설 풍경  (0) 2011.02.27
생일선물  (0) 2011.02.01
내 생일 @2011.1.22  (2) 2011.01.23
MT  (0) 2011.01.23
Posted by 모근원

COCOSDenshion (of cocos2d 0.99.5) 의 SimpleAudioEngine 믿고 배경음을 출력했었다.
시뮬레이터에서 잘 되고~ 아이팟 터치 2세대에서 잘 되고~

그런데!! iPhone 4에서만 배경음악이 출력이 안되는것~! (효과음 effect 출력은 잘 되고있는데?!)
로그를 찍어보니 파일의 위치를 읽어오지 못하고 있었다.
참고로 한 Sample 의 TomTheTurret 도 마찬가지의 버그를 내고있었고..
버그인지.. cocos2d 라이브러리를 직접 수정하다가. 곧 업데이트가 될것 같아서 원복하고..
그냥 임시로 업데이트될때까지 SimpleAudioEngine 에서 배경음 재생 실패할 경우 AVAudioPlayer 를 이용해서 재생하도록 했다.
그리고 Fadein/out 시켜주는 메소드 하나 추가하고..
결과는 기종에 관계없이 배경음이 자~알 출력된다.
사운드 엔진은 싱글턴으로 프로그램내에서 어디서든지 불러다 쓸수있게 했다.

SimpleAudioEngine *soundEngine_;



//-- 이 아래에는 AVAudioPlayer를 위한 멤바들.

BOOL isAVAudioPlayer; //한번이라도 AVAudioPlayer 이용하여 배경음을 재생하면 셋팅된다.

//차선책으로 쓰는 AVAudioPlayer

AVAudioPlayer *bgmPlayer;

CGFloat fadeAmt; //Fadeout 감소값.

CGFloat fadeDesc; //Fadein 목표값.

요러한 멤버객체가 인터페이스에 선언이 되어있다 치고..

-(SimpleAudioEngine *) soundEngine

요런식으로 심플 오디오 엔진을 리턴하는 메소드가 있다 하자..

객체 init 해줄때 초기화 꼼꼼히 해주고..

isAVAudioPlayer = NO;

fadeAmt = 0;


dealloc  해줄땐 AVAudioPlayer 로 배경음 재생중이면 꺼주고 죽여주자.

if (self.bgmPlayer != nil) {

[self stopBGMwithAudioPlayer];

}




발로짠 오디오 플레이어 부분.

//배경음 재생을 시작한다.

-(void)playBGMwithAudioPlayer:(NSString*)filename ext:(NSString*)ext volume:(CGFloat)vol{

if (self.isSoundOn) {

[self stopBGMwithAudioPlayer];

if(self.bgmPlayer == nil){

self.bgmPlayer = [self createAudioPlayer:filename ext:ext volume:vol];

//-1 무한반복

self.bgmPlayer.numberOfLoops = -1;

}

[self.bgmPlayer play];

}

}


-(void)stopBGMwithAudioPlayer{

if (self.isSoundOn && self.bgmPlayer != nil) {

NSLog(@"AVAudioPlayer 스탑!");

[self.bgmPlayer stop];

// self.bgmPlayer.currentTime = 0; //rewind

self.bgmPlayer = nil;

}

}



//오디오 플레이어를 만든다.

-(AVAudioPlayer*)createAudioPlayer:(NSString*)filename ext:(NSString*)ext volume:(CGFloat)vol{

NSString *audioPath = [[NSBundle mainBundle]pathForResource:filename ofType:ext];

NSLog(@"새로운 AVAudioPlayer 생성 %@.%@ (%f%%)",filename,ext,vol);

AVAudioPlayer *tmpAudioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:audioPath] error:nil];

tmpAudioPlayer.numberOfLoops = 0;

tmpAudioPlayer.volume = vol;

//소리를 위해 버퍼에 로딩

[tmpAudioPlayer prepareToPlay];

isAVAudioPlayer = YES;

[tmpAudioPlayer autorelease];

return tmpAudioPlayer;

}



//음악을 서서히 죽여준다.

-(void)fadeoutBGMwithAudioPlayer{

    if (self.bgmPlayer!=nil && self.bgmPlayer.volume > 0.01f) {

//2초정도 소리를 줄여주기 위하여

if (fadeAmt == 0) fadeAmt = self.bgmPlayer.volume/20;

        self.bgmPlayer.volume = self.bgmPlayer.volume - fadeAmt;

        [self performSelector:@selector(fadeoutBGMwithAudioPlayer) withObject:nil afterDelay:0.1f];

} else {

NSLog(@"fadeOut finish");

fadeAmt = 0;


[self stopBGMwithAudioPlayer];

}

}



//음악을 서서히 살려준다.

-(void)fadeinBGMwithAudioPlayer{

//NSLog(@"fadeIn %f amount %f desc %f",self.bgmPlayer.volume,fadeAmt,fadeDesc);

    if (self.bgmPlayer!=nil && self.bgmPlayer.volume <= fadeDesc) {

//2초정도 소리를 줄여주기 위하여

if (fadeAmt == 0) fadeAmt = fadeDesc/20;

        self.bgmPlayer.volume = self.bgmPlayer.volume + fadeAmt;

        [self performSelector:@selector(fadeinBGMwithAudioPlayer) withObject:nil afterDelay:0.1f];

} else {

NSLog(@"fadeIn finish");

fadeAmt = 0;

}

}


그리고 SimpleAudioPlayer 를 같이 쓰면서 배경음 재생 실패하면 바로 AVAudioPlayer 를 이용.

//BGM재생.

-(void) playBGM:(NSString*)filename ext:(NSString*)ext volume:(CGFloat)vol{

if (self.isSoundOn){

//soundEngine

if (!isAVAudioPlayer && [self soundEngine] != nil) {

NSLog(@"soundEngine 으로 배경음 재생 시도.");

[self soundEngine].backgroundMusicVolume = vol;

[[self soundEngine] playBackgroundMusic:[[NSBundle mainBundle] pathForResource:filename ofType:ext] loop:YES];

}

NSLog(@"soundEngine : isBackgroundMusicPlaying (%d), isAVAudioPlayer(%d)",[[self soundEngine] isBackgroundMusicPlaying],isAVAudioPlayer);

if (isAVAudioPlayer || [self soundEngine] == nil || ([self soundEngine] != nil && ![[self soundEngine] isBackgroundMusicPlaying])) {

//AudioPlayer

NSLog(@"soundEngine 으로 배경음 재생 실패하여 AVAudioPlayer 시도.");

[self playBGMwithAudioPlayer:filename ext:ext volume:vol];

}

}

}


-(void) fadeinBGM:(NSString*)filename ext:(NSString*)ext volume:(CGFloat)vol{

if (self.isSoundOn){

//soundEngine

if (!isAVAudioPlayer && [self soundEngine] != nil) {

NSLog(@"soundEngine 으로 배경음 페이드 재생 시도.");

[self soundEngine].backgroundMusicVolume = 0;

[[self soundEngine] playBackgroundMusic:[[NSBundle mainBundle] pathForResource:filename ofType:ext] loop:YES];

[CDXPropertyModifierAction fadeBackgroundMusic:2.0f finalVolume:vol curveType:kIT_SCurve shouldStop:NO];

}

if (isAVAudioPlayer || [self soundEngine] == nil || ([self soundEngine] != nil && ![[self soundEngine] isBackgroundMusicPlaying])) {

//AudioPlayer

NSLog(@"soundEngine 으로 배경음 재생 실패하여 AVAudioPlayer 페이드 재생 시도.");

fadeDesc = vol;

[self playBGMwithAudioPlayer:filename ext:ext volume:0];

[self fadeinBGMwithAudioPlayer];

}

}

}


-(void) stopBGM{

if (self.isSoundOn){

if (isAVAudioPlayer) {

[self stopBGMwithAudioPlayer];

} else {

if ([self soundEngine] != nil){

[[self soundEngine] stopBackgroundMusic];

}

}

}

}


-(void) fadeoutBGM {

if (self.isSoundOn) {

if (isAVAudioPlayer) {

[self fadeoutBGMwithAudioPlayer];

} else {

if ([self soundEngine] != nil){

//2 페이드 아웃.

[CDXPropertyModifierAction fadeBackgroundMusic:2.0f finalVolume:0.0f curveType:kIT_SCurve shouldStop:YES];

}

}

}

}


끗. (이랬는데 뭔가 잘못써서 SimpleAudioEngine 에서 출력이 안된거라면 대략 난감.. 삽질의 향기가..)


- (덧) COCOS 2D 1.0rc 버전에서는 수정되었음 -_-......... 
Posted by 모근원
새로산 카메라 Sony NEX-5 수령후 테스트샷~
일단 조그맣고 가볍고 잘나오는거 같아 다행이다. 생각외로 만족감을 주는 기기 :-)
이제 g35만 사면 되겠다.

아이뽄4


iHub


내 맥북이


애물단지 매직 마우스


토로


분사저지?


사과밭 책상위


맥북 상판 테스트샷


나는 커피 내려마시는 남자

책상 테스트 샷.


Sony NEX-5 / E18-55mm F3.5-5.6 OSS / RAW / Lightroom 3 / 2011. 11. 6


'내가찍은사진 > 디카사진들' 카테고리의 다른 글

커피 대량 구입  (0) 2011.01.20
2011.1.8 데이트 @홍대,대학로  (0) 2011.01.11
대학모임  (2) 2011.01.11
작년 여름.  (2) 2011.01.03
정신없는 하루하루  (0) 2010.06.20
지금은 곤란하다.  (6) 2010.03.17
Posted by 모근원

- 이번 글은 보기좋게 PDF로 첨부합니다.




- 개인적인용도로 요약한 글이라 글에서는 경어체를 사용하지 않습니다. 글 읽으시는데 참고부탁드립니다.

- Mac의 Pages 로 작성한 후 블로그에 포스팅하려니 서식이 다 깨졌네요.

   PDF 파일로 보는것이 보기 좋습니다.


저번엔 안드로이드용 위치기반 지점찾기 (LBS)를 구현하였고, 이번에 아이폰용 뱅킹어플을 만들면서 아이폰용도 지점찾기를 어플로 구현할 필요가 생겼다.


이번엔 계속 써와서 익숙한 Java 가 아니라 Objective C 여서 시작하기가 막막했다. 배우면서, 삽질하며 완성시킨거라 버그도 있을것이고 여러부분에서 미숙한 점이 있을테지만 마찬가지로 까먹지 않기 위하여 정리를 해둔다.


1. 프로젝트에 프레임웍 추가하기.

프로젝트의 프레임웍에서 마우스 오른쪽버튼 (또는 옵션클릭)을 하여 프레임웍을 추가해준다.

사용자 위치정보를 가져올 CoreLocation.framework 와 지도표시에 필요한 MapKit.framework 을 추가해준다.


추가가 된것을 확인하면 성공.










2. 뷰에서 사용할 마커(어노테이션) 준비하기.


지도 앱들을 보면 다음과 같은 핀이 있는데 이것이 안드로이드에서는 마커, iOS에서는 어노테이션이라고 불리우는 드랍핀이다. 


그냥 써도 되지만 지점찾기 앱에서는 각 마커마다 지점의 정보를 가지고 있기

때문에 MKAnnotation 을 구현하여 커스텀 어노테이션을 만들어 쓰기로 했다.



//  BranchMarker.h

// 마커(어노테이션) 쓰일 객체.


#import <Foundation/Foundation.h>

#import <MapKit/MKAnnotation.h>


@interface BranchMarker : NSObject <MKAnnotation>{

//요거 세개는 어노테이션에 필수로 구현해줘야 동작한다.

CLLocationCoordinate2D coordinate;

NSString *title;

NSString *subtitle;

// 아래는 추가로 필요해서 변수 준비.

NSString *bussBrNm; //영업점명

NSString *bussBrTelNo; //영업점 전화번호

NSString *bussBrAdr; //영업점주소 (찾아오시는길)

NSString *trscDrtm; //거래시간

NSString *bussBrAdr2; //영업점주소 (주소)

NSString *markerType; //마커 타입 (0:지점, 1:ATM)

}


@property (nonatomic,assign) CLLocationCoordinate2D coordinate;

@property (nonatomic,copy) NSString *title;

@property (nonatomic,copy) NSString *subtitle;


@property (nonatomic,retain) NSString *bussBrNm;

@property (nonatomic,retain) NSString *bussBrTelNo;

@property (nonatomic,retain) NSString *bussBrAdr;

@property (nonatomic,retain) NSString *trscDrtm;

@property (nonatomic,retain) NSString *bussBrAdr2;

@property (nonatomic,retain) NSString *markerType;


@end

헤더에서는 coordinate, title, subtitle 을 필수로 구현해줘야 MKAnnotation 이 멀쩡히 돌아간다.


//  BranchMarker.m

#import "BranchMarker.h"


@implementation BranchMarker

@synthesize coordinate, title, subtitle;

@synthesize bussBrNm,bussBrTelNo,bussBrAdr,trscDrtm,bussBrAdr2,markerType;


-(void) dealloc{

[title release];

[subtitle release];

[super dealloc];

}


@end

구현파일에서는 특별히 구현할것이 없고 synthesize 만 충실히 해주도록 한다.



3. 뷰컨트롤러 준비하기.

이제 실제 지도를 구현해본다. 이번 어플에서는 크게 다음과 같이 네개의 뷰가 겹쳐져 있다.

맨 아래에 지도를 표시하는 MKMapView 가 깔리고 그 위로 서브뷰로 아이콘 버튼들이 있는 툴바,

그리고 툴바위에 역 지오코딩 (위도, 경도를 가지고 주소를 추적해내는 기술) 한 스트링이 UILabel 로 뿌려지고, 마지막으로 그 위에 어플이 로딩상태일때 로딩을 표시할 스피너가 올려져있다.


//  BranchMapViewController.h

// 지점찾기 컨트롤러.


#import <UIKit/UIKit.h>

#import <MapKit/MapKit.h>

#import <CoreLocation/CoreLocation.h>


//위치관리자, 맵뷰, 그리고 리버스 지오코더 딜리게이트를 구현한다.

@interface BranchMapViewController : UIViewController <CLLocationManagerDelegate , MKMapViewDelegate, MKReverseGeocoderDelegate>{

NSString *searchType; //지점,ATM 검색 타입

MKMapView *mapView; //지도

//,경도를 가지고 해당위치의 주소를 가지고 오는 리버스지오코더

MKReverseGeocoder *reverseGeocoder

//위지관리자. GPS,wifi 등으로 현재 기기의 위치를 가져온다.

CLLocationManager *locationManager;

CLLocation *lastScannedLocation; //마지막으로 검색된 위치를 저장할 객체.

UIActivityIndicatorView * spinner; //화면의 로딩 스피너.

UILabel *geoLabel; //툴바에 리버스지오코더의 결과를 표시한다.

}


@property (retain, nonatomic) NSString *searchType;

@property (retain, nonatomic) MKMapView *mapView;

@property (nonatomic, retain) MKReverseGeocoder *reverseGeocoder;

@property (nonatomic, retain) CLLocationManager *locationManager;

@property (nonatomic, retain) CLLocation *lastScannedLocation;

@property (nonatomic, retain) UIActivityIndicatorView * spinner;

@property (nonatomic, retain) UILabel *geoLabel;


//뷰컨트롤러를 만들때 검색타입을 지정한다. BRANCH/ATM

- (id)initWithShowType:(NSString *)showType;  

//지점정보를 HTTP통신으로 가지고 온다.
- (void)getBranchDataWithLocation:(CLLocation *)location; 

@end


메인 구현파일이라 엄청길다.

//  BranchMapViewController.m

#import "BranchMapViewController.h"

#import <MapKit/MapKit.h>

#import <CoreLocation/CoreLocation.h>

#import "BranchMarker.h"

#import "BranchMapGetDataAction.h"


@implementation BranchMapViewController


@synthesize searchType;

@synthesize mapView,reverseGeocoder,geoLabel;

@synthesize locationManager;

@synthesize lastScannedLocation;

@synthesize spinner;


- (id)initWithShowType:(NSString *)showType {

if ((self = [super init])) {

        // Custom initialization

self.searchType = showType;

    }

NSLog(@"initWithShow %@",self.searchType);

    return self;

}


//이미지로 커스텀 뷰를 만들어준다.

//_normalImg : 버튼 이미지, _touchImg : 눌럿을때 바뀔 이미지, _width : 이미지버튼의 가로길이, _height : 이미지버튼의 세로길이 , _sel : 버튼눌렀을때 액션

-(UIButton*) createCustomImageButtonWithNormalImgNm:(NSString*)_normalImg

  andTouchImg:(NSString*)_touchImg andWidth:(float)_width

andHeight:(float)_height andSEL:(SEL)_sel{

// 버튼 배경에 사용할 이미지 준비.

    UIImage *normalImage = [UIImage imageNamed:_normalImg];

    UIImage *touchImage = [UIImage imageNamed:_touchImg];

    

    // 버튼 생성 

//x,y,width,height

    CGRect buttonRect = CGRectMake(0.0f, 0.0f, _width, _height); 

    UIButton *button = [[[UIButton alloc

initWithFrame:buttonRect] autorelease];

    // 버튼의 배경 이미지 설정

    [button setBackgroundImage:normalImage forState:UIControlStateNormal];

    [button setBackgroundImage:touchImage forState:UIControlStateHighlighted];

    

    // 버튼에 액션 설정

[button addTarget:self action:_sel

forControlEvents:UIControlEventTouchUpInside];


return button;

}


- (void)viewDidLoad {

    [super viewDidLoad];

//searchType 널탕이 들어오면 기본적으로 지점 검색으로 한다.

if (self.searchType == nil) self.searchType = @"BRANCH";

//위치 관리자를 초기화한다.

self.locationManager = [[[CLLocationManager alloc] init] autorelease];

//딜리게이트는 self 설정후 하단에서 딜리게이트 구현.

self.locationManager.delegate = self;

//측정방법은 가장 좋게.

self.locationManager.desiredAccuracy = kCLLocationAccuracyBest

//2000m 이상 위치가 변경되면 노티를 .

self.locationManager.distanceFilter = 2000.0f

    [self.locationManager startUpdatingLocation]; //현재위치 가져오기 시작~

//지도 뷰를 만든다.

//뷰의 크기만큼 지도를 채운다.

mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];

mapView.showsUserLocation = YES; // 위치 표시.

[mapView setMapType:MKMapTypeStandard]; //지도 형태는 기본.

[mapView setZoomEnabled:YES]; //줌가능

[mapView setScrollEnabled:YES]; //스크롤가능

mapView.delegate = self; //딜리게이트 설정 (anotation 메소드를 구현한다.)

MKCoordinateRegion region;

MKCoordinateSpan span; //보여줄 지도가 처리하는 넓이 정의.

span.latitudeDelta = 0.02; //숫자가 적으면 좁은영역 까지 보임.

span.longitudeDelta = 0.02;

CLLocationCoordinate2D location = mapView.userLocation.coordinate;

//위치정보를 못가져왔을때 기본으로 보여줄 위치.

location.latitude = 37.566275; //37.490481 이건 우리집

location.longitude = 126.981794; //126.857790

region.span = span; //크기 설정.

region.center = location; //위치 설정.

[mapView setRegion:region animated:TRUE]; //지도 뷰에 지역 설정.

[mapView regionThatFits:region]; //지도 화면에 맞게 크기 조정.

[self.view addSubview:mapView]; //서브 뷰로 지도를 추가함.

//하단에 버튼들 toolbar 추가

//현재 뷰의 크기를 가져와서 상단 바의 길이가 조정되면 하단 바가 잘리는것을 방지하기 위함.

float heightPos = self.view.bounds.size.height

UIToolbar *toolbar = [[UIToolbar alloc

  initWithFrame:CGRectMake(0.0, heightPos - 50.0f , 320.0, 50.0)]; toolbar.barStyle = UIBarStyleBlackTranslucent; //툴바스타일은 까만 투명색

// 영역 잡아주는 버튼아이템. 왼쪽에 빈 영역 두고, 오른쪽으로 버튼들을 배치하기위함.

UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]

  initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace

  target:nil action:nil];

 

//이미지 커스텀 버튼.

UIBarButtonItem *hereBtn = [[UIBarButtonItem alloc]

   initWithCustomView:[self createCustomImageButtonWithNormalImgNm:@"here.png"

   andTouchImg:@"here_pressed.png" andWidth:40.0f andHeight:40.0f

   andSEL:@selector(setSearchTypeToHere)]]; //현위치

UIBarButtonItem *branchBtn = [[UIBarButtonItem alloc]

   initWithCustomView:[self createCustomImageButtonWithNormalImgNm:@"atm_btn.png" 

   andTouchImg:@"atm_btn_pressed.png" andWidth:40.0f andHeight:40.0f 

   andSEL:@selector(setSearchTypeToATM)]]; //ATM검색

UIBarButtonItem *atmBtn = [[UIBarButtonItem alloc]

   initWithCustomView:[self createCustomImageButtonWithNormalImgNm:@"hana_btn.png"

   andTouchImg:@"hana_btn_pressed.png" andWidth:40.0f andHeight:40.0f 

   andSEL:@selector(setSearchTypeToBranch)]]; //지점검색

//툴바 아이템 배치

toolbar.items = [NSArray

arrayWithObjects:flexibleSpace,hereBtn,atmBtn,branchBtn,nil];


//툴바를 뷰에 추가.

[self.view addSubview:toolbar];

//툴바에 쓰인 버튼들 릴리즈.

[flexibleSpace release];

[hereBtn release];

[branchBtn release];

[atmBtn release];

[toolbar release];

//화면스피너 셋팅. 로딩중을 표시하기 위함.

self.spinner = [[UIActivityIndicatorView alloc

initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];

//화면중간에 위치하기위한 포인트.

[self.spinner setCenter:CGPointMake(320.0f/2.0, 480.0f/2.0)]; 

[self.view addSubview:spinner]; //스피너를 뷰에 추가하고 필요시에 start

//geoCoder 라벨 셋팅. '서울시 송파구 신천동' 따위를 툴바에 표시한다

geoLabel = [[UILabel alloc

initWithFrame:CGRectMake(5.0, heightPos - 45.0f, 160.0, 40.0)];

geoLabel.backgroundColor = [UIColor clearColor];

geoLabel.highlighted = YES;

geoLabel.highlightedTextColor = [UIColor whiteColor];

geoLabel.shadowColor = [UIColor blackColor];

geoLabel.textColor = [UIColor whiteColor];

geoLabel.textAlignment = UITextAlignmentLeft;

geoLabel.numberOfLines = 2; //두줄 표시 가능.

[self.view addSubview:geoLabel]; //뷰에 라벨 추가.

//초기 환영 메세지.

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"위치기반 지점찾기" message:@"위치정보를 가져오는데 기기,통신상태에 따라 시간이 걸릴수 있으며 일부 동작하지 않는 기기도 있습니다.\n\n하단의 아이콘을 이용하여 현재 지도가 표시하고 있는 지역을 중심으로 지점/ATM 검색하실 있습니다." delegate:nil cancelButtonTitle:nil

otherButtonTitles:@"확인",nil];

[alert show];

[alert release];

}


//검색 타입 ATM으로 셋팅.

-(void)setSearchTypeToATM{

//현재 지도가 위치하는곳을 중심으로.

CLLocation *customLocation = [[CLLocation alloc

initWithLatitude:mapView.centerCoordinate.latitude 

longitude:mapView.centerCoordinate.longitude];

self.searchType = @"ATM";

[self getBranchDataWithLocation:customLocation]; //HTTP 통신

[customLocation release];

}


//검색 타입 지점으로 셋팅.

-(void)setSearchTypeToBranch{

//현재 지도가 위치하는곳을 중심으로.

CLLocation *customLocation = [[CLLocation alloc

initWithLatitude:mapView.centerCoordinate.latitude 

longitude:mapView.centerCoordinate.longitude];

self.searchType = @"BRANCH";

[self getBranchDataWithLocation:customLocation]; //HTTP 통신

[customLocation release];

}


//현위치

-(void)setSearchTypeToHere{

[self.locationManager startUpdatingLocation];  //로케이션 메니저 다시 시작~

}


//문자열 치환 메소드. source : 원본, 찾을문자열, 바꿀문자열.

-(NSString*)replaceStrSource:(NSString*)sourceStr 

strFrom:(NSString*)_from strTo:(NSString*)_to{

NSMutableString *mstr = [NSMutableString stringWithString:sourceStr];

NSRange substr = [mstr rangeOfString: _from];

while (substr.location != NSNotFound) {

[mstr replaceCharactersInRange: substr withString:_to];

substr = [mstr rangeOfString: _from];

}

return mstr;

}



//지도 데이터를 HTTP통신을 통해 받아와서 표시해준다.

- (void)getBranchDataWithLocation:(CLLocation *)location{

NSLog(@"getBranchDataWithLatitude:%f andLongitude:%f",

location.coordinate.latitude,location.coordinate.longitude);

//화면에 로딩스피너 스타트.

[self.spinner startAnimating];

//HTTP통신에 ContentProvide server 규격을 맞추기 위해, 위도,경도에서 콤마(.) 제거해서 보내야한다.

NSString *lat = [self replaceStrSource:

[NSString stringWithFormat:@"%f",location.coordinate.latitude]

strFrom:@"." strTo:@""];

NSString *lng = [self replaceStrSource:

[NSString stringWithFormat:@"%f",location.coordinate.longitude]

strFrom:@"." strTo:@""];

NSString *range = @"3000"; //기본 3Km반경 지점을 검색해 오게 만든다.

NSString *sType = @"0";

//ATM = 1, 지점 = 0

if ([self.searchType isEqualToString:@"ATM"]) sType = @"1";

else sType = @"0";


//HTTP통신으로 지점정보 가져오는 액션 초기화.

BranchMapGetDataAction *getAction = [[BranchMapGetDataAction alloc

initWithSearchType:sType andReqLat:lat andReqLng:lng andReqRange:range];

//HTTP통신으로 지점정보를 가져온다.

NSMutableArray *branchMarkerAry = [getAction getData];

//마커를 새로 찍기전에 기존에 지도에 있던 마커(annotation) 전부 지운다.

NSMutableArray *toRemove = [NSMutableArray arrayWithCapacity:1];

for(id annotation in mapView.annotations){

if (annotation != mapView.userLocation){

[toRemove addObject:annotation];

}

}

NSLog(@"remove %d annotations.",[toRemove count]);

[mapView removeAnnotations:toRemove];

//받아온 마커(annotation) 맵에 찍어낸다.

NSLog(@"branch marker count : %d",[branchMarkerAry count]);

if([branchMarkerAry count] > 0){

for (BranchMarker* marker in branchMarkerAry){

if (marker != nil) [mapView addAnnotation:marker];

}

}


//reverseGeocoding 시작.

self.reverseGeocoder = [[[MKReverseGeocoder alloc

initWithCoordinate:location.coordinate] autorelease];

    reverseGeocoder.delegate = self;

    [reverseGeocoder start];

//화면의 로딩 스피너 없애기.

[self.spinner stopAnimating];


}


//메모리 부족을 받았을때.

- (void)didReceiveMemoryWarning {

    // Releases the view if it doesn't have a superview.

    [super didReceiveMemoryWarning];

    NSLog(@"branchmap memory warning.");

    // Release any cached data, images, etc that aren't in use.

}


// 내릴때.

- (void)viewDidUnload {

    

NSLog(@"branchmap viewDidUnload");

[self.locationManager stopUpdatingLocation];

self.locationManager = nil;

self.reverseGeocoder = nil;

self.mapView = nil;

self.searchType = nil;

self.lastScannedLocation = nil;

self.spinner = nil;

[super viewDidUnload];

}


//객체 내려갈때.

- (void)dealloc {

NSLog(@"branchmap dealloc");

//사용한 객체들 릴리즈.

[mapView release];

[reverseGeocoder release];

[locationManager release];

[searchType release];

[lastScannedLocation release];

[spinner release];

    [super dealloc];

}



#pragma mark MKMapViewDelegate


NSString *tempTelNo; //어노테이션의 더보기에서 전화걸기를 누를때 임시로 전화번호를 저장할 변수.


//맵의 어노테이션 (마커) 표시.

-(MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id<MKAnnotation>)annotation{

if (annotation==self.mapView.userLocation){

[mV.userLocation setTitle:@"현재 위치"]; //현재위치 마커에 표시할 타이틀.

return nil; //현재 위치 마커일경우 커스텀 마커를 사용하지 않는다.

}

//현재위치 마커가 아닐때에는 지점마커이다.

BranchMarker *mk = (BranchMarker *) annotation;

MKPinAnnotationView *dropPin = nil; //마커 준비

static NSString *reusePinID = @"branchPin"; //마커 객체를 재사용 하기위한 ID

//마커 초기화

dropPin = (MKPinAnnotationView *)[mapView 

dequeueReusableAnnotationViewWithIdentifier:reusePinID]; 

if ( dropPin == nil ) dropPin = [[[MKPinAnnotationView alloc]

initWithAnnotation:annotation reuseIdentifier:reusePinID] autorelease];

//핀이 떨어지는 애니메이션

dropPin.animatesDrop = YES;

//마커 오른쪽에 (>) 모양 버튼 초기화.

UIButton *infoBtn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

dropPin.userInteractionEnabled = TRUE;

dropPin.canShowCallout = YES;

dropPin.rightCalloutAccessoryView = infoBtn;

//마커 왼쪽에 표시할 지점,ATM 아이콘

NSString* markerImg = nil;

if ([mk.markerType isEqualToString:@"0"]){

markerImg = @"hana.png";

dropPin.pinColor = MKPinAnnotationColorGreen;

} else {

markerImg = @"atm.png";

dropPin.pinColor = MKPinAnnotationColorRed;

}

dropPin.leftCalloutAccessoryView = [[[UIImageView alloc

initWithImage:[UIImage imageNamed:markerImg]] autorelease];


//마커 리턴

return dropPin;

}



//어노테이션의 더보기

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view

calloutAccessoryControlTapped:(UIControl *)control{

BranchMarker *mk = (BranchMarker *) view.annotation;

tempTelNo = nil;

//얼럿메세지 초기화

NSString *alertMessage = [mk.title stringByAppendingString:@"\n"]; 

if ([mk.bussBrAdr length] > 1) //주소

alertMessage = [[alertMessage stringByAppendingString:@"\n"]

stringByAppendingString:mk.bussBrAdr];

if ([mk.trscDrtm length] > 1) //ATM운영 시간

alertMessage = [[alertMessage stringByAppendingString:@"\nATM : "

stringByAppendingString:mk.trscDrtm]; 

NSString* telTitle = nil; //전화걸기 버튼 타이틀.

if ([mk.bussBrTelNo length] > 1){ //전화번호

alertMessage = [[alertMessage stringByAppendingString:@"\n대표전화 : "]

stringByAppendingString:mk.bussBrTelNo];


telTitle = @"전화걸기";

}

tempTelNo = mk.bussBrTelNo;

//얼럿뷰 표시

UIAlertView *confirmDiag = [[UIAlertView alloc] initWithTitle:nil

message:alertMessage delegate:self cancelButtonTitle:@"닫기" 

otherButtonTitles:telTitle, nil];


[confirmDiag show];

[confirmDiag release];

}


//어노테이션의 더보기 (얼럿뷰) 에서 버튼 클릭.

-(void)alertView:(UIAlertView *)alertView 

clickedButtonAtIndex:(NSInteger)buttonIndex{

if (buttonIndex == 1){

NSLog(@"전화걸기 : %@",tempTelNo);


if (tempTelNo != nil){

[[UIApplication sharedApplication

openURL:[NSURL URLWithString:[@"tel:" 

stringByAppendingString:tempTelNo]]];

}

} else if (buttonIndex == 0) {

NSLog(@"닫기");

}

}


#pragma mark LocationManager

//위치가 변경되었을때 호출.

-(void)locationManager:(CLLocationManager *)manager

didUpdateToLocation:(CLLocation *)newLocation 

fromLocation:(CLLocation *)oldLocation {


NSString *strInfo = [NSString 

stringWithFormat:@"didUpdateToLocation: latitude = %f, longitude = %f",

newLocation.coordinate.latitude, newLocation.coordinate.longitude];

NSLog(@"%@",strInfo);


MKCoordinateRegion region; //레젼설정

region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, 2000, 2000);

MKCoordinateRegion adjustedRegion = [mapView regionThatFits:region];

[mapView setRegion:adjustedRegion animated:YES];

//마지막으로 검색된 위치를 다른곳에서 활용하기 위하여 설정.

self.lastScannedLocation = newLocation; 


//한번 위치를 잡으면 로케이션 매니저 정지.

[self.locationManager stopUpdatingLocation];

[self getBranchDataWithLocation:self.lastScannedLocation]; //화면에 마커찍기

}


//위치를 못가져왔을때 에러 호출.


-(void)locationManager:(CLLocationManager *)manager

  didFailWithError:(NSError *)error{

NSLog(@"locationManager error!!!");

//위치를 못가져왔을땐 현재 지도에 표시된 지역기준으로 지점검색 들어간다~

[self setSearchTypeToBranch];

//에러 다이얼로그 표시.

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"위치기반 지점찾기" message:@"현재위치를 검색할수 없습니다.\n설정 > 일반 > 위치서비스 활성화 되어있는지 확인해주세요.\n\n위치정보를 가져올수 없어도 하단의 아이콘을 통하여 현재 지도의\n영업점/ATM 위치는 검색하실수\n있습니다." delegate:nil cancelButtonTitle:nil otherButtonTitles:@"확인",nil];

[alert show];

[alert release];

}


#pragma mark reverseGeocoder

//역지오코더 검색되었을때 UILabel 역지오코딩 내용 표시

-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder

didFindPlacemark:(MKPlacemark *)placemark{


    if (geoLabel != nil){

//혹시 몰라 한번 try 싸줌.

@try {

NSString *geoString = @"";

//locality 서울특별시 subLocality 송파구 thoroughfare 신천동

//지역에 따라 특정 파라메터에 값이 없을 있음. nil체크 하여 표시함.

if (placemark.locality != nil

geoString = [[geoString 

stringByAppendingString:placemark.locality

stringByAppendingString:@" "];

if (placemark.subLocality != nil)

geoString = [[geoString 

stringByAppendingString:placemark.subLocality]

stringByAppendingString:@"\n"];

if (placemark.thoroughfare != nil)

geoString = [geoString

stringByAppendingString:placemark.thoroughfare];

//아무 정보도 받아올수 없으면 나라이름이라도 표시.

if ([geoString length] < 1 && placemark.country != nil)

geoString = placemark.country;

geoLabel.text = geoString; //UILabel 표시

}

@catch (NSException * e) {

//오류 발생하면 UILabel 비워줌.

NSLog(@"reverse GeoCoding error : %@",e);

geoLabel.text = nil;

}

@finally {

}

}

}


//역지오코더 에러 발생시 그냥 로그.


-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder

didFailWithError:(NSError *)error{

    NSLog(@"MKReverseGeocoder has failed.");

}


@end



4. 데이터 받아오는 액션 준비하기.

 지점 데이터는 HTTP통신으로 받아오게 된다.

예를 들어 http://111.11.11.11:8888/getBranch.do?a=123&b=456 이런식으로 URL을 호출하게 되면 서버에서 리턴값이 스트링으로 “S;10;테스트지점;02-123-4567;서울시 구로구 개봉동;....”  이런식으로 세미콜론(;) 으로 구분된 문자로 내려오게 된다.

그러면 프로그램에서 해당 스트링을 잘라서 객체에 잘 집어넣으면 된다. 

이것은 컨덴트 서버와 규격을 맞추어 프로그래밍을 해야한다.

하나은행에서 쓰이는 지점정보 서버와의 통신은 대외비이므로 지도구현과 관계없는 부분은 생략하여 정리한다.

//  BranchMapGetDataAction.h

// HTTP 통신으로 컨덴츠 서버에서 데이터를 받아서 어노테이션에 셋팅하는 액션


#import <Foundation/Foundation.h>


@interface BranchMapGetDataAction : NSObject{

NSString *searchType; //검색조건

NSString *reqLat; //요청 위도

NSString *reqLng; //요청 경도

NSString *reqRange; //요청 범위 (메타 m 단위)

}


@property (nonatomic,retain) NSString *searchType;

@property (nonatomic,retain) NSString *reqLat;

@property (nonatomic,retain) NSString *reqLng;

@property (nonatomic,retain) NSString *reqRange;


- (id)initWithSearchType:(NSString *)_searchType andReqLat:(NSString *)

_reqLat andReqLng:(NSString *)_reqLng andReqRange:(NSString*)

_reqRange; //초기화 메소드

- (NSMutableArray*)getData; //데이터를 가져오는 메소드

- (NSString*)generateGeoCode:(NSString*)str; //서버의 응답 스트링 지오코드에 콤마 붙이는 메소드.


@end



//  BranchMapGetDataAction.m


#import "BranchMapGetDataAction.h"

#import "BranchMarker.h"

#import <MapKit/MapKit.h>


@implementation BranchMapGetDataAction

@synthesize searchType,reqLat,reqLng,reqRange;


//초기화 메소드.


(id)initWithSearchType:(NSString *)_searchType 

andReqLat:(NSString *)_reqLat andReqLng:(NSString *)_reqLng 

andReqRange:(NSString*)_reqRange {


if ((self = [super init])) {

        // Custom initialization

self.searchType = _searchType;

self.reqLat = _reqLat;

self.reqLng = _reqLng;

self.reqRange = _reqRange;

    }

    return self;

}


// 결과값 받아다가 어노테이션(마커) 배열로 리턴.

- (NSMutableArray *)getData{

//스테이더스 바에 로딩 표시. (데이터 가져오는 네트워크 상태 표시)

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

//요청타입이 널탕이면 기본적으로 지점검색으로 셋팅.

if (self.searchType == nil || [self.searchType isEqualToString:@""]){

self.searchType = @"0";

}

//요청 URL

NSString *urlString = @"http://1.1.1.1/a/b.jsp?distance=";

urlString = [[urlString stringByAppendingString:self.reqRange]

stringByAppendingString:@"&map_x="];

urlString = [[urlString stringByAppendingString:self.reqLng]

stringByAppendingString:@"&map_y="];

urlString = [[urlString stringByAppendingString:self.reqLat]

stringByAppendingString:@"&svc_type="];

urlString = [urlString stringByAppendingString:self.searchType];

NSURL *url = [NSURL URLWithString:urlString];

NSLog(@"url : %@", urlString);

//리퀘스트 객체.

NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]

initWithURL:url] autorelease];

//레스폰스 객체,에러 객체 준비.

NSURLResponse *response = nil;

NSError *error = nil;

//데이터 받아오기.

NSData* receiveData = [NSURLConnection sendSynchronousRequest:request

returningResponse:&response error:&error];

//받아온 데이터 파싱.

NSString *str = [[NSString alloc] initWithData:receiveData 

encoding:0x80000000 + kCFStringEncodingDOSKorean];

str = [str stringByReplacingPercentEscapesUsingEncoding:

0x80000000 + kCFStringEncodingDOSKorean];


//NSLog(@"DATA GETTED!!! : %@",str);

//에러가 발생하였으면 에러표시.

if(error != nil) {

NSLog(@"%@", [error localizedDescription]);

UIAlertView *alert = [UIAlertView alloc];

[alert initWithTitle:@"에러" message:[NSString 

stringWithFormat:@"서버에 접속할 없습니다.\n%@",

[error localizedDescription]] delegate:self 

cancelButtonTitle:@"확인" otherButtonTitles:nil];

[alert show];

[alert release];

}


//마커배열 준비.

//받아온 스트링을 세미콜론으로 잘라서 배열로 넣어버린다.

NSArray *branchArray = [str componentsSeparatedByString:@";"]; 

NSMutableArray *returnAry = [[NSMutableArray alloc] init]; //리턴할 배열 준비.

NSLog(@"getted branch array size : %d",[branchArray count]);

@try {

//i=2 준것은 첫번째 배열엔 성공여부(S) 두번째 배열엔 받아온 지점 갯수 (int#) 이다

안쓰이므로 무시하고 세번째 배열원소부터 사용하도록한다.

for (int i=2; i<([branchArray count]-1); i+=7) { 

//마커 준비.

BranchMarker *marker = [[BranchMarker alloc] init];

// 셋팅.

marker.bussBrNm = [branchArray objectAtIndex:i];

marker.bussBrTelNo = [branchArray objectAtIndex:i+1];

marker.bussBrAdr = [branchArray objectAtIndex:i+3];

marker.bussBrAdr2 = [branchArray objectAtIndex:i+2];

marker.trscDrtm = [branchArray objectAtIndex:i+4];

//마커에 위도,경도 정보 셋팅.

MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };

region.center.latitude = [[self generateGeoCode:

[branchArray objectAtIndex:i+6]] floatValue];

region.center.longitude = [[self generateGeoCode:

[branchArray objectAtIndex:i+5]] floatValue];

region.span.longitudeDelta = 0.01f;

region.span.latitudeDelta = 0.01f;

marker.coordinate = region.center; //셋팅!

//찾아오시는길은 값이 있을때에만 셋팅.

if ([ marker.bussBrAdr length] > 1

marker.subtitle = marker.bussBrAdr;

marker.markerType = self.searchType; //마커 타입 (지점/ATM)

if ([self.searchType isEqualToString:@"0"]){

//지점이면 이름에다가 "지점" 이라는 글씨 추가로 셋팅.

marker.title = [marker.bussBrNm 

stringByAppendingString:@" 지점"];

} else {

marker.title = marker.bussBrNm;

}

//배열에 추가.

[returnAry addObject:marker];

//마커 릴리즈.

[marker release];

}

}

@catch (NSException * e) {

//가끔 컨덴츠 서버에서 오류가 데이터를 내리는 경우가 있다.에러,보정처리는 알아서~

.....삭제.....

}

@finally {

}




//검색결과가 없을때 오류 표시.

if ([returnAry count] == 0){

NSString *errorTitle = nil;

NSString *errorMsg = @"\n\n네트워크 오류일수 있으니 다른지역으로 이동, 또는 지도를 확대하여\n검색하시거나 잠시 다시 시도해주세요.";

if ([self.searchType isEqualToString:@"0"]){

errorTitle = @"영업점 검색오류";

errorMsg = [[NSString stringWithString:

@"해당 지역에 '영업점' 검색결과가\n없습니다."] stringByAppendingString:errorMsg];

} else {

errorTitle = @"ATM 검색오류";

errorMsg = [[NSString stringWithString:

@"해당 지역에 'ATM' 검색결과가\n없습니다."] stringByAppendingString:errorMsg];

}

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:errorTitle

message:errorMsg delegate:nil 

cancelButtonTitle:nil

otherButtonTitles:@"확인",nil];

[alert show];

[alert release];

}

//스테이더스바 로딩 끄기.

[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

//배열 리턴.

return returnAry;

}



// 위도 경도에 콤마 붙이기. ex(37123456 -> 37.123456)

-(NSString*)generateGeoCode:(NSString*)str {

if (str != nil && [str length] >= 8) {

int lastIdx = [str length];

int middIdx = lastIdx - 6;

NSString* s1 =[str substringWithRange:

NSMakeRange (0,middIdx)]; //콤마 앞의 스트링

NSString* s2 =[str substringWithRange:

NSMakeRange (middIdx,6)]; //콤마 뒤의 스트링

NSString *output = [[s1 stringByAppendingString:@"."]

stringByAppendingString:s2]; //콤마 붙여서 리턴

return output;

}

return nil;

}


@end



5. 실행 스크린샷.

최초 실행하면 나오는 안내문구.



현재위치를 표시하며 현재위치 기준으로 영업점들을 찾아서 주루룩 찍어낸다.



현재위치 마커엔 title 로 "현재위치" 라고 셋팅 해 두었다.



지점/ATM 마커를 누르면 간단한 정보가 나온다.



간단한 정보에서 오른쪽 버튼을 누르면 상세한 정보가 나온다.



선택 지점/ATM으로 전화걸기



현위치 말고도 지도를 옮겨 원하는 지역에서 검색을 할 수도 있다.



원하는 지역으로 옮겨가서 하단의 지점/ATM 아이콘을 누르기만 하면 된다.



마지막으로 우리동네도 한번.



6. 해결하지 못한 부분


- MKMapview 의 지도화면에 특정 지점을 눌러서 뭔가 액션을 해주고 싶어 MKMapview를 상속하여 커스텀 맵뷰를 구현했는데 터치이벤트에서 오류 작렬! 그리고 줌인 줌아웃시에 오류가 난다.. ㅠ


- iOS 4부터는 스테이터스 바에 위치서비스 사용 아이콘이 나오는데 분명 LocationManager 를 stop 시켜주었는데도 아이콘이 계속 떠있다. Tweeter 어플등을 보면 현위치를 가져오고 난 뒤에는 아이콘이 사라지는것 같은데..

solution : 

mapView.showsUserLocation = YES;

이 문제였다. 현위치가 필요하지 않을때 적당한 시점에서 NO 를 넣어주면 현재위치 가져오는것을 종료하고 스테이더스바에 위치서비스 아이콘이 없어지게 된다.


- 3G 네트워크 등. 인터넷 상태가 불안정할때 처리에 오래 걸리는 문제. 데이터를 받아오는 순간에 프로그램이 정지된것 처럼 보인다. iOS 도 스레드를 돌려서 백그라운드로 돌려야 하나? 이건 다음버전에서 고민.


- 이번 글은 보기좋게 PDF파일로 첨부합니다.




2010.11.17 모근원 (Geunwon,Mo)

mokorean@gmail.com

twitter : @mokorean

http://Lomohome.com



* 추가  TIP : Google 로고 옮기기.
현재 툴바로 구글로고를 가려지게 되어있는데 이게 앱스토어에 올라갈경우 리젝사유가 된다고 한다.
그래서 바로 구글링해서 구글로고 옮기는법을 찾았다.



먼저 MKMapView 에다가 메소드를 추가할것이니 카테고리로 구현하도록 한다.
나는 클래스를 추가하였다.

//

//  BranchMapMKMapView.h

//  BranchMap

//

//  Created by Geunwon,Mo on 10. 11. 18..

//  Copyright 2010 Lomohome.com. All rights reserved.

//


#import <MapKit/MapKit.h>


@interface MKMapView (Additions) 


- (UIImageView*)googleLogo;


@end


//

//  BranchMapMKMapView.m

//  BranchMap

//

//  Created by Geunwon,Mo on 10. 11. 18..

//  Copyright 2010 Lomohome.com. All rights reserved.

//


#import "BranchMapMKMapView.h"



@implementation MKMapView (Additions)


- (UIImageView*)googleLogo {

UIImageView *imgView = nil;

for (UIView *subview in self.subviews) {

if ([subview isMemberOfClass:[UIImageView class]]) {

imgView = (UIImageView*)subview;

break;

}

}

return imgView;

}


@end


카테고리 추가후 지도를 구현하는 ViewController  (여기서는 BranchMapViewController.m)에다가 카테고리로 추가한 메소드를 뷰가 나오기전에 실행해서 구글로고의 위치를 변경하도록 한다.

#import "BranchMapGetDataAction.h"


....생략.....



////////////// Custom MapView Category start ///////////////

float _toolBarPositionY = 0.0f; //<- 이놈은 viewDidLoad 안에서 값을 셋팅한다. 현재 화면 툴바의 Y좌표값.


- (void)viewDidAppear:(BOOL)animated {

NSLog(@"view did appear");

[self relocateGoogleLogo];

}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {

[self relocateGoogleLogo];

}


- (void)relocateGoogleLogo {

UIImageView *logo = [mapView googleLogo];

if (logo == nil)

return;

CGRect frame = logo.frame;

frame.origin.y = _toolBarPositionY - frame.size.height - frame.origin.x;

logo.frame = frame;

}

////////////// Custom MapView Category end ///////////////



Posted by 모근원
지난 포스트를 보면 웹뷰에서 자바스크립트 alert 을 웹뷰에서 재정의 해서 썼는데
개발을 하다가 난관에 부딫혔다. 자바스크립트의 alert 말고, confirm 을 사용할때에도 재정의가 필요해졌다.

confirm 창은 특성상, '예','아니오' 버튼이 나오고 이 버튼이 눌리면 버튼에 따라 액션을 달리 취해주어야한다.

그런데 UIAlertView 를 show 하게 되면 예, 아니오 버튼과 상관없이 밑의 코드가 주~욱 실행되버려서 예,아니오의 리턴값을 받을수가 없다. (말로 표현하자니.. 이거 영..)


억지로 버튼을 누르기전까지 루프를 돌려서 지연을 시키고, UIAlertView 가 사라지는 시점에 버튼의 결과를 가지고와서 처리하는것으로 변경한 코드.
분명 더 좋은 방법이 있을것 같은데 ㅠ 못찾겠다.

*헤더부분은 생략.

//@Geunwon,Mo 2010.9.17 : UIWebView Javascript alert 위한 카테고리.

@implementation UIWebView (JavaScriptAlert)

.... 저번포스트 생략 ....

static BOOL diagStat = NO; //예,아니오 버튼의 상태를 저장할 임시 변수

- (BOOL)webView:(UIWebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame{

NSLog(@"javascript ConfirmPanel : %@",message);

UIAlertView *confirmDiag = [[UIAlertView alloc] initWithTitle:nil message:message delegate:self cancelButtonTitle:NSLocalizedString(@"Yes", @"") otherButtonTitles:NSLocalizedString(@"No", @"아니오"), nil];

[confirmDiag show];

//버튼 누르기전까지 지연.

while (confirmDiag.hidden == NO && confirmDiag.superview != nil)

[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];

[confirmDiag release];

return diagStat;

}



//요놈은 UIAlertViewDelegate 를 구현하여 버튼이 눌렸을때 실행될 메소드

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

//index 0 : YES , 1 : NO

if (buttonIndex == 0){

//return YES;

diagStat = YES;

} else if (buttonIndex == 1) {

//return NO;

diagStat = NO;

}

}

@end


동작순서는 웹뷰에서 confirm 자바스크립트 함수가 호출이 되면 카테고리로 구현한 webView: runJavaScriptConfirmPanelWithMessage 메소드가 호출이되고,
UIAlertView 로 메세지창(+예,아니오)을 띄우고 while 문을 통하여 메세지 창이 없어질때까지 루프..;;
그리고 메세지창에서 버튼을 누르면 alertView: clickedButtonAtIndex 메소드가 호출이 되고 미리 준비해둔 전역변수에 버튼에 따라 상태값을 저장.
메세지창이 닫히면서 while 루프가 끝나게 되고 전역변수의 값을 자바스크립트로 리턴. 하게된다.


* 더 좋은 방법이 있으시면 공유 부탁드립니다. ㅠㅠ
Posted by 모근원
iPhone 어플에서 UIWebView 를 사용한 프로그램에서
웹뷰안에서 JavaScript 의 Alert 를 호출 하면  (ex -> javascript:alert("test some strings.");)
다음과 같이 호스트 이름이 얼럿창의 상단에 찍히게 된다.

보안때문에 상단의 ip 는 지웠지만 만약 웹뷰에서 lomohome.com  에서 얼럿창을 띄웠다면  lomohome.com 이라는 문구가 얼럿창의 타이틀에 뜨게 된다.

이 얼럿창은 UIWebView 에 카테고리를 이용하여 딜리게이트 메소드를 구현해주면 수정할 수 있다.
먼저 UIWebView 의 헤더파일에 다음과 같은 인터페이스를 추가해준다.

//@Geunwon,Mo 2010.9.17 : UIWebView Javascript alert 위한 카테고리.

@interface UIWebView (JavaScriptAlert) 

- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame;

@end


그리고 구현파일에 구현해준다.
 

//@Geunwon,Mo 2010.9.17 : UIWebView Javascript alert 위한 카테고리.

@implementation UIWebView (JavaScriptAlert)

- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {

NSLog(@"javascript alert : %@",message);

    UIAlertView* customAlert = [[UIAlertView alloc] initWithTitle:@"Hana Bank" message:message delegate:nil cancelButtonTitle:@"확인" otherButtonTitles:nil];

    [customAlert show];

    [customAlert autorelease];

}

@end


 요렇게 구현하고 나면 다음과 같이 Title  에 원하는 문구로 자바스크립트 얼럿을 띄울 수 있다.



구현 메소드 중, 타이틀 인자를 nil 로 넣으면 아무것도 안찍힌다.

UIAlertView* customAlert = [[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:@"확인" otherButtonTitles:nil];




Posted by 모근원


지금은 곤란하다. 조금만 기다려달라.
4G 가 나올때까지 버텨주길 바랬는데... ㅠ

'내가찍은사진 > 디카사진들' 카테고리의 다른 글

카메라 테스트  (0) 2011.01.10
작년 여름.  (2) 2011.01.03
정신없는 하루하루  (0) 2010.06.20
로모 살아나다  (2) 2010.03.16
괴물 핸드폰 아이폰?  (4) 2009.12.09
애플 아이폰  (0) 2009.12.03
Posted by 모근원

아이팟이나 아이폰에서 티스토리 블로그를 보시는분들 많으시죠?
http://[일반주소].tistory.com
를 사용하시던 티스토리 블로거 님들은 신경쓰지 않으셔도
아이팟이나 아이폰에서 블로그 접근하면 모바일용 페이지로 보입니다.
스마트폰에서 블로그로 접근하면 모바일용 페이지로 보입니다.

- 수정 : 현재나와있는 거의 모든 모바일기기의 userAgent 를 검사하는 방식으로 변경했습니다.
http://en.wikipedia.org/wiki/List_of_user_agents_for_mobile_phones

그런데 저처럼 티스토리에 자신의 도메인을 연결해서 쓰시는분들은
다음 그림에서처럼 자동으로 모바일 페이지로 이동이 안되고 일반 웹페이지가 나왔었지요.


이때 자바스크립트 몇줄만 스킨에 넣어주면 모바일용 페이지로 이동하게 만들 수 있습니다.
이 방법은 저처럼 lomohome.com 이나 www.lomohome.com 등의 도메인을 티스토리에 연결하신 분께 해당됩니다.

관리자모드 - 스킨 - HTML/CSS 편집에서
HTML 의 <head></head> 태그 사이안에 다음의 자바스크립트를 넣으면

블로그 접근시 유저에이전트를 검사하여 스마트폰이면
모바일 사이트로 이동을 하게 합니다 :-)

<script language="JavaScript">
//모바일 페이지로 이동.
//http://en.wikipedia.org/wiki/List_of_user_agents_for_mobile_phones
var uAgent = navigator.userAgent.toLowerCase();
var mobilePhones = new Array('iphone','ipod','android','blackberry','windows ce',
        'nokia','webos','opera mini','sonyericsson','opera mobi','iemobile');
for(var i=0;i<mobilePhones.length;i++)
    if(uAgent.indexOf(mobilePhones[i]) != -1)
        document.location = "http://"+ location.host +"/m" + location.pathname;
</script>

주의하실점은 위의 빨간색에 이탤릭체로 표시된 부분은
http://[원래티스토리주소].tistory.com/m
으로 해주셔야 한다는것 입니다 :-)
url 을 가져와서 가는방법으로 수정되었습니다.

* 현재 제 블로그에서 [페이지 소스보기] 로 보셔도 해당 소스를 보실 수 있습니다.

작업 후 아이팟 터치나 아이폰에서 자신의 블로그로 접근하면

작업 후 스마트폰에서 자신의 블로그로 접근하면
이렇게 모바일용 페이지로 이동합니다 :-)

모바일용 웹페이지는 작은 해상도에 맞게 이미지등도 리사이즈 해줘서
느린 모바일기기에서 최적의 상태로 블로그를 볼수 있게 합니다.

Posted by 모근원
국내 은행 최초로 하나은행에서 아이폰/아이팟 용 모바일뱅킹 어플이 나왔습니다.
이름은 하나 N Bank 라는군요.
아시는 바와같이 해킹 (즉 탈옥)된 아이폰/팟 (이하는 편의상 아이폰이라 하겠습니다) 에서는 사용이 되지 않는다네요. 보안을 위해서라 생각됩니다.
처음엔 한국 스토어에만 올라왔으나 현재는 미국스토어에서도 내려받기가 가능하다고 합니다.
가격은 당연히 Free 이구요.
어플스토어에서 "hanabank" 를 검색하시면 됩니다.
http://itunes.apple.com/us/app/id340826757?mt=8 [바로가기 링크]

실행하면 기동화면이 나오고 해킹여부를 확인후 공인인증센터에서 인증서를 내려받을수 있습니다.
이니텍의 모듈을 사용하기때문에 안타깝게도 Windows 기반의 PC 에서 작업을 해야하네요.
공인인증서를 아이폰에 넣는 방법은 하나은행 홈페이지에 상세히 설명 되어있습니다.
http://www.hanabank.com/smart

이제 공인인증서를 내려받았으니 기본적인 거래내역 조회와 이체화면을 알아보겠습니다.

공인인증서 작업이 완료된 후엔 이렇게 인증서 로그인을 통해 뱅킹서비스를 시작하게됩니다.


인증서 선택후 인증서 비밀번호를 입력하는 화면입니다.


12시부터 5분간은 서비스가 안된다네요 -_-;


최초 사용시엔 이렇게 약관페이지가 나오며
약관에 동의하여야 스마트폰 뱅킹 서비스를 받을 수 있습니다.


스마트폰 뱅킹 서비스를 가입하기 위해 다시 한번 공인인증서와 보안카드를 넣는
보안매채인증 작업을 합니다. OTP 이용자는 OTP 번호 입력창이 뜨겠네요.
탈옥을 하면 사용할수 없는 모바일 뱅킹이라 Backgrounder 를 사용할수 없습니다.
저처럼 아이폰에 보안카드 번호를 보안어플을 이용하여
보관하시는 분들은 조금 불행한겁니다.. 실물 카드를 가지고 다니는편이 편하겠네요.

*간단하지만 복잡한-_-팁을 하나 알려드리겠습니다.
보안카드란것은 한번 요구번호가 나오면 (위에서는 19,18) 정확한 보안카드번호를 넣어서 거래를 하기전엔 바뀌지 않습니다. 따라서 번거롭지만 아무데나 이체를 하는척해서 보안카드 요구번호를 알아내고 어플 종료후, 보안카드 어플에서 보안카드 번호를 확인한후 다시 은행어플로 와서 진행하면 아까 요구한것과 같은 보안카드 요구번호가 나올겁니다. 조금 귀찮지요..


어찌되었건 사용완료 하고 이젠 사용을 하면 됩니다 :-)


예금계좌 목록조회 화면입니다.
하나은행에 가지고있는 통장목록이 나오는군요.
탭으로 예금,펀드,대출,외환,자산의 보기가 가능합니다.
통장에서 오른쪽의 > 버튼을 누르면 해당 통장에서 할수있는 바로가기 버튼들이 나옵니다.
월급통장의 > 를 탭하니 계좌정보,거래내역,즉시이체 버튼이 나오는군요.


계좌정보를 조회한 화면입니다.
해당 통장의 상세정보가 나옵니다.


거래내역 조회를 해보겠습니다.
인터넷뱅킹과 화면이 그리 다르지 않습니다.
계좌가 여러개이신 분들은 계좌번호선택에 번호만 표기되어 불편한 점도 있을 수 있겠네요.


조회 결과입니다.
출금된 내역은 - 로, 입금된내역은 + 로 표시가 되네요.
아이폰을 이용하면 어디서든 입출금 내역을 확인할수가 있습니다.


자금이체의 첫화면입니다.
인터넷뱅킹과 화면이 다르지 않습니다 :-)
'자주쓰는 계좌' 나 최근입금계좌를 사용이 가능하네요.
자주쓰는 계좌를 눌러보겠습니다.


인터넷뱅킹에서 관리되는 자주쓰는 계좌와 완전히 동일합니다.
자주쓰는 계좌는 이렇게 등록하여 쓰면 클릭만으로 은행과 계좌번호가 자동입력 됩니다.


은행 선택화면입니다.


금액이나 계좌번호를 입력할땐 Qwerty 키보드가아닌 숫자키패드가 나타납니다.
상당히 편리한 인터페이스네요 :-) 개발자분들의 노고와 세심함이 느껴지는 부분입니다.


어찌돼었든, 이체를 진행해보겠습니다.
수취조회를 한 화면입니다. 인터넷뱅킹 이체의 step 2 에 해당하겠네요.
여기서 받는 분을 확인하고 '진행'하게되면 실제 이체 프로세스가 진행됩니다.


보안카드 입력화면이네요.
실물카드를 가지고 있지 않고 폰에 보안카드 번호가 있다면 조금 불편함이 예상됩니다.
보안카드를 입력하고 다시한번 공인인증서를 제출하게 됩니다.


이체가 완료된 화면입니다.
속도도 3G 망을 이용하여 테스트 했는데 크게 불편이 없는 속도였습니다.
10여분넘게 인터넷뱅킹을 테스트 했었는데 사용된 패킷은
일반 > 사용내역 확인시 2메가 남짓 된듯 보입니다. 2MB 정도 사용하니 웬지 찝찝하네요 -_-..


이 포스팅으로 간단히 하나은행 아이폰 뱅킹에서 거래내역 조회와 이체화면을 살펴봤습니다.
인터넷뱅킹을 사용할때 주로 하는 거래가 이 두가지 거래라고 생각합니다.
이 외에 지로/공과금 납부나 카드서비스, 외환, 펀드등의 서비스가 곧 추가될것으로 보이니 국내 은행중 첫 아이폰 뱅킹서비스를 선보이는것 치고는 굉장한 완성도를 보입니다 :-)
이로서 아이폰을 사용하는데 불편함이 하나 해소되었네요.


'내가끄적인글 > 끄적인 글' 카테고리의 다른 글

R.I.P.  (0) 2011.10.06
맥북 프로 15인치 구입.  (0) 2010.09.06
iPad 구입 :-)  (11) 2010.04.17
HD-DVD addon for XBOX360  (6) 2008.12.08
[OSX] dell mini9 99% 동작하는 사운드 드라이버  (4) 2008.11.19
[OSX] dell mini9 CPU speed step 설정.  (0) 2008.11.19
Posted by 모근원
그동안 써왔던 핸드폰중에 사진이 이렇게 나오는 핸드폰은 아이폰이 처음인듯 싶다.

출근길 지하철 안에서. 꽉막히던 강변북로.


새벽에 집을 나서면서


점심먹고 나와 식당앞에서 한컷


명동에 함박눈 내리던 오후


사무실 로비에 자리잡은 크리스 마스 트리


Apple iPhone 3Gs / sRGB / Free PhotoFilters +photoshop.com / 2009. 12

'내가찍은사진 > 디카사진들' 카테고리의 다른 글

정신없는 하루하루  (0) 2010.06.20
지금은 곤란하다.  (6) 2010.03.17
로모 살아나다  (2) 2010.03.16
애플 아이폰  (0) 2009.12.03
영등포 교보문고  (0) 2009.11.04
프로그래머의 자리  (13) 2009.11.04
Posted by 모근원
아기다리고기다리던 아이폰이 왔습니다 :-)

드디어 왔구나~ iPhone 3Gs.


정갈한 32기가 화이트입니다. 밥주고 있지요 :-)


이날 같이 배송된 애플 "매직 마우스"
버튼과 스크롤휠이 없고 멀티터치 펑션이 들어가있습니다.


사무실 책상샷. 절반은 애플제품이군요 :-)


아이폰 구입으로 방출된 아이팟 터치 1세대.
첫월급 받아서 산이후 애지중지한게 2년이 되어가네요 :-)


안녕~


박스에 담아 고이고이 빠이빠이 ㅎ


그나저나 핸드폰 산지 5개월만의 기기변경이라 기기할부 20만원 + 위약금 12만원을 냈더니
아이폰을 구입하기 위해 출혈이 심했다는 생각도 드네요 ;-(


SIGMA DP2 / RAW / sRGB / SPP 3.3 + Photoshop CS3 / 2009. 12. 1


'내가찍은사진 > 디카사진들' 카테고리의 다른 글

지금은 곤란하다.  (6) 2010.03.17
로모 살아나다  (2) 2010.03.16
괴물 핸드폰 아이폰?  (4) 2009.12.09
영등포 교보문고  (0) 2009.11.04
프로그래머의 자리  (13) 2009.11.04
가을 북한산 등산  (4) 2009.10.30
Posted by 모근원
고우교에서 출사를 마치고, JR도쿄역으로 이동하는중에 갑자기 비가 오기 시작했다.
도쿄 날씨는 알수 없다던데, 그래도 다행이 이 비가 여행중 유일한 비였다.
하늘도 여행을 돕는지 비는 금방 그쳤다.


사용자 삽입 이미지




사용자 삽입 이미지

지나가다 빅카메라에서. 이날 (7월 11일) 은 전 세계적으로 iPhone 3G가 발매하는 날이었다.
우리나라 빼놓고 말이지. ㅠㅠ



사용자 삽입 이미지

긴자 사거리 도착.



사용자 삽입 이미지

긴자의 애플센터. 큼직큼직 하다.



사용자 삽입 이미지

비온뒤의 하늘.



사용자 삽입 이미지

긴자 산쵸메.



사용자 삽입 이미지

복잡하면서 깔끔함을 유지하고 있는 쇼핑거리였다.



사용자 삽입 이미지

삿포로 맥주.



사용자 삽입 이미지

LG



사용자 삽입 이미지

다양한 간판이 길을 수놓았다.



사용자 삽입 이미지

도쿄디즈니랜드 호텔이 그랜드 오픈.



사용자 삽입 이미지

아사쿠사로 이동.
아사쿠사는 긴자와는 반대로 예전 문화들을 거리에 고스란히 담고 있었다.



사용자 삽입 이미지

가미나리몬 전경.



사용자 삽입 이미지

가미나리몬. 어느 곳보다 외국인이 많은걸 볼수 있었다.



사용자 삽입 이미지

붉은 등이 인상적이다.



사용자 삽입 이미지

가미나리몬을 통과, 센소지까지 가는길인  나카미세 거리.



사용자 삽입 이미지

다양한 기념품들을 팔고 있었는데. 일본 하면 단연 고양이.



사용자 삽입 이미지

기모노. 마쯔리 기간이라 기모노 입은 사람들을 많이 볼수 있었다.



사용자 삽입 이미지

센소지 도착.
이 문은 센소지의 호조몬.
센소지는 628년에 세워진 도쿄에서 가장 오래된 사찰이라고 한다.



사용자 삽입 이미지

노을지는 센소지 5층탑



사용자 삽입 이미지

호조문.



사용자 삽입 이미지

금이다 금.



사용자 삽입 이미지

대형 짚신.



사용자 삽입 이미지

센소지 경내에 도착.



사용자 삽입 이미지

무엇을 빌고 계신가요.



사용자 삽입 이미지

무서워서 물 뜨겠나 이거.



사용자 삽입 이미지

센소지 우측 부분.



사용자 삽입 이미지

오래된 절이라고는 하지만 계속 되는 보수로 새 건물이나 마찬가지의 포스를 풍겼다.



사용자 삽입 이미지

아.. 이게 이름이 뭐더라.



사용자 삽입 이미지

뭔가. 좀.



사용자 삽입 이미지

센소지 주변은 에도시대 거리 풍경이 살아있었다.



사용자 삽입 이미지

딱봐도 일본. 이라는 느낌.



사용자 삽입 이미지

왜색이 짙다라는 표현은 이럴때 쓰면 좋을것 같다. 진한 풍경.



사용자 삽입 이미지

오래된 종로거리를 보는듣한 느낌이었다. 마음에 든다.



사용자 삽입 이미지

처음오는곳인데도 묘한 향수가 느껴지는듯한 거리였다.
옛날의 시골 풍경이랄까.



사용자 삽입 이미지

아사쿠사 엔게이홀.
실제 '만담' 이나 '가부키'등의 일본 전통 공연을 하는 공연장이다.
에도시대부터 대중문화를 이끌던 곳이라고 한다.



사용자 삽입 이미지

엔게이홀



사용자 삽입 이미지

도쿄 여행중 아사쿠사 풍경이 제일 마음에 들었다.



사용자 삽입 이미지

자판기의 나라.



사용자 삽입 이미지

여기저기 기모노.



사용자 삽입 이미지

네온사인이 인상적이었다.



사용자 삽입 이미지

카미야바.
찍을땐 그저 옛분위기가 나는 바구나 하고 찍었는데,
 알아보니 1967년 오픈한 오래되고 유명한 곳으로 소설이나 영화에도 많이 등장한다고 한다.



사용자 삽입 이미지

거대한.. 콧물. 정도.



사용자 삽입 이미지

수상버스 타는곳 건너편엔 횟집들이 있었다.



사용자 삽입 이미지

물위에 떠있는 횟집.
문득 인천이 생각났다.



사용자 삽입 이미지

장소를 옮겨 옮겨. 이곳은 도쿄 인터네셔널 포럼.



사용자 삽입 이미지

2016년 올림픽 유치를 위해 여기저기 광고물들을 볼수 잇었다.



사용자 삽입 이미지

도쿄에서 토론토 친구 쇼코를 만나 시부야에서 한잔.
길건너 저 많은 사람들이. 다.
길 건너려고 기다리는 사람들. -_-



사용자 삽입 이미지

오랬만에 만난 친구와 한잔.



사용자 삽입 이미지

맥주한잔 하고 노래방을 갔는데 노래방 시스템이 우리나라와는 조금 달랐다.
리모콘에 번호를 다 넣고 예약을 하면 기계로 번호가 전송되는 시스템.



사용자 삽입 이미지

한국 노래도. 딱 두페이지 -_- 있었다.



사용자 삽입 이미지

근데 우리나라 노래방과는 다르게 가사 박자가 한박자 빠르게 지나간다는 점.


Nikon D70s / 18-55mm 1:3.5-5.6 / JPG HIGH / `08.7.11
Posted by 모근원