이 블로그 검색

2011년 7월 31일 일요일

Android - jar 파일 추가 하는 방법 (퍼옴)



여지껏 이것도 몰랐다... ㅡㅡ;


Android 에 jar 파일 추가하는 방법
(eclipse version : helios) 

1. 원하는 프로젝트 선택후 오른쪽버튼 클릭 -> Properties 클릭




2. java Build Path 클릭 -> Add External JaRs 클릭 ->
    추가를 원하는 jar 파일을 찾아 열기버튼 클릭



3. 추가된 jar 파일 확인후 Ok 버튼 클릭



4. android project 에 추가된 jar 파일 확인후 필요한
    Open Source 를 사용하시면 됩니다.

2011년 7월 26일 화요일

안드로이드 홈키버튼과 같은 효과 내기

홈키 버튼을 누르지 않고 일반 UI 버튼을 눌렀을 때도 홈키 버튼과 같은 기능(즉, 앱은 실행된 상태로 있고 바탕화면으로 이동하는 ..) 을 구현하는 로직.

다음과 같이 Intent를 날려주면 된다.. ㅎㅎ

       Intent intent = new Intent();
        intent.setAction("android.intent.action.MAIN");
        intent.addCategory("android.intent.category.HOME");
        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
        | Intent.FLAG_ACTIVITY_FORWARD_RESULT
        | Intent.FLAG_ACTIVITY_NEW_TASK
        | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP
        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        startActivity(intent);

Intent에 관련된 다양한 플래그들과 액션이 많다... ;;

2011년 7월 19일 화요일

xcode 단축키


2008/02/10

Xcode 단축키(hotkey)

1. Xcode

1. 프로젝트 & 파일

새 프로젝트 [shift][command] N
새로운 프로젝트를 생성한다.
프로젝트 닫기 [control][command] W
현재 프로젝트를 종료한다.
프로젝트 추가 [option][command] A
프로젝트에 파일을 추가 한다.
새 파일 [command] N
새로운 파일을 작성한다.
파일열기 [command] O
파일을 오픈한다.
파일 저장 [command] S
현재 파일을 저장한다.
모든 파일 저장 [option][command] S
프로젝트의 모든 변경된 파일을 저장한다.


2. Xcode

새 그룹 생성 [option][command]  N
좌측의 Groups & Fiels에서 새로운 그룹을 생성한다.
파일그룹 지정 [option][command] G
선택된 파일들을 새로운 그룹으로 만든다.
파일그룹 지정 [option][command][shift]G
그룹을 해제한다.
에디터 전체 보기 [shift] [command] E
Group & Files 윈도우를 제외하고 에디터를 전체 보기로 만든다.
에디터 전체 보기 [shift] [option] [command] E
Group & Files 윈도우도 감추고 에디터를 전체 보기로 만든다.
클래스 브라우져 [shift] [command] C
프레임워크 또는 사용자 정의 클래스의 메소드, 소스를 보여 준다. 
target 속성창 [option][command] E
빌드 및 어플리케이션 옵션을 설정한다.
실행 속성창 [option][command] X
실행파일 패스, 인자, 디버깅 정보를 설정한다.
핼프 [shift] [command] /
Xcode 핼프 윈도우를 연다.


3. 빌드

빌드 [command] B
프로젝트를 빌드 한다.
빌드 후 실행 [command] R
빌드를 완료한 후, 프로그램을 실행한다.
빌드 후 디버그 [command] Y
빌드를 완료한 후, 디버그를 실행한다.
빌드 결과 보기 [shift][command] B
빌드 결과창을 보여준다. 
clean [shift][command] K
소스 파일의 날짜를 현재 시간으로 변경하여, 다시 컴파일 될 수 있도록 한다.


4. 디버깅

다음 경고/오류 [command] =
소스파일에서 경고나 오류가 발생한 다음 위치로이동한다.
이전 경고/오류 [shift][command] =
소스파일에서 경고나 오류가 발생한 이전 위치로 이동한다.
디버거 [shift][command] Y
디버거 윈도우로 이동한다.
디버그 실행 [option][command] Y
디버그를 시작한다.
브레이크포인트 보기 [option][command] B
브레이크포인트가 설정된 위치를 모두 보여 준다.
브레이크포인트 [command] \
현재 위치에 브레이크포인트를 설정/해제 한다.
step into [shift][command] I 
한라인씩 실행하며 함수일 경우 내부로 진입한다. 
step over [shift][command] O
한라인씩 실행하며 함수일 경우 건너 뛴다.
step out [shift][command] T
함수를 나가 호출한 위치 다음으로 이동한다. 
로그창 [shift][command] R
실행로그창을 보여 둔다.

5. 에디터

잘라내기 [command] X
현재 선택된 영역을 잘라낸다.
복사 [command] C
현재 선택된 영역을 복사낸다.
붙여넣기 [command] V
현재 클립보드에 있는 내용을 붙여 넣는다.
undo [command] Z
실행을 취소한다.
redo [shift] [command] Z
undo를 취소한다.
전체선택 [command] A
파일 전체를 선택한다.
자동완성 보기 [esc]
자동으로 입력될 내용(함수, 변수)들을 보여 준다.
다음 제안 [control] .
자동완성 목록중에 다음 목록을 보여 준다.
이전/다음 파일 [option] [command] 왼쪽 방향키
여러 파일이 열려있을 경우에 다음 소스 파일을 보여 준다.
이전/다음 파일 [option] [command] 오른쪽 방향키
여러 파일이 열려있을 경우에 이전 소스 파일을 보여 준다.
헤더/소스 파일 보기 [option] [command] 위쪽 방향키
소스파일일 경우에는 헤더 파일을 헤더파일일 경우에는 소스 파일을 보여 준다.
우측으로 들여 쓰기 [command] ]
현재 또는 선택된 영역의 내용을 오른쪽으로 한칸 들여 쓴다.
좌측으로 들여 쓰기 [command] [
현재 또는 선택된 영역의 내용을 왼쪽으로 한칸 들여 쓴다.
찾기 [command] F
현재 에디터에서 문자를 찾거나 대치한다.
전체 찾기 [shift][command] F
모든 프로젝트 또는 열린 파일에서 문자를 찾거나 대치한다.
다음 문자 [command] G
다음 문자를 찾는다.
이전 문자 [shift][command] G
이전 문자를 찾는다.
찾기 문자열에 복사 [command] E
현재 선택된 문자를 찾을 문자열 에디터에 복사한다.
대치 문자열에 복사 [command] J
현재 선택된 문자를 대치할 문자열 에디터에 복사한다.
라인 가기 [command] L
지정한 라인으로 바로 간다.
북마크에 추가 [command] D
현재 라인을 북마크에 추가한다.


2. 인터페이스 빌더
1) Window

인스펙터 [shift][command] I
오브젝트의 속성창을 연다.
attributes [command] 1
인스펙터의 attributes 항목을 연다. 
connections [command] 2
인스펙터의 connections 항목을 연다.
size [command] 3
인스펙터의 size 항목을 연다.
bindings [command] 4
인스펙터의 bindings 항목을 연다.
팔레트 [command] /
팔레트 윈도우를 보여준다.
테스트 [command] R
윈도우를 실행한다.
테스트 종료 [command] Q
윈도우를 종료하고 인터페이스 빌더로 돌아 온다.
정렬 판넬 [shift][command] A 
윈도우 상의 오브젝트들을 정렬할 수 있는 판넬을 연다.
레이아웃 사각형 [command] L
오브젝트들의 레이아웃을 나타내는 사각형을 보여주거나 감춘다.
레이아웃 검사 [shift][command] V
 레이아웃 검사창은 보여준다. 오브젝트들의 겹침, 잘림등을 체크해준다.

2)  Classes

subclass [option][command] S
Classes 판넬에서 현재 선택된 클래스의 서브클래스를 생성한다.
Action 추가 [option][command] A
Classes 판넬에서 현재 선택된 클래스에 Action을 추가한다.
Outlets 추가 [option][command] O
Classes 판넬에서 현재 선택된 클래스에 Outlet을 추가한다.
Outlets 추가 [option][command] F
Classes 판넬에서 현재 선택된 클래스의 소스파일을 생성한다.
출처 :http://www.cocoadev.co.kr/52

테이블 뷰 몇가지 UI 요소

* 테이블 뷰 셀에 더보기 표시 ('>') 만들기

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

  
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

}

* 테이블 뷰 셀 클릭시 나왔다 사라지게

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

}  

MKMapView 완전 정리

아래 이분께서 넘 정리 잘 하셔 놓음...

http://lomohome.com/321

혹시 링크 날아갈까봐 그대로 복사해놓음..



- 개인적인용도로 요약한 글이라 글에서는 경어체를 사용하지 않습니다. 글 읽으시는데 참고부탁드립니다.
- 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 (retainnonatomic) NSString *searchType;
@property (retainnonatomic) MKMapView *mapView;
@property (nonatomicretain) MKReverseGeocoder *reverseGeocoder;
@property (nonatomicretain) CLLocationManager *locationManager;
@property (nonatomicretain) CLLocation *lastScannedLocation;
@property (nonatomicretain) UIActivityIndicatorView * spinner;
@property (nonatomicretain) 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.0f0.0f, _width, _height); 
    UIButton *button = [[[UIButton alloc
initWithFrame:buttonRect] autorelease];
    // 버튼의 배경 이미지 설정
    [button setBackgroundImage:normalImage forState:UIControlStateNormal];
    [button setBackgroundImage:touchImageforState:UIControlStateHighlighted];
    
    // 버튼에 액션 설정
[button addTarget:self action:_sel
forControlEvents:UIControlEventTouchUpInside];
return button;
}
- (void)viewDidLoad {
    [super viewDidLoad];
//searchType  널탕이 들어오면 기본적으로 지점 검색으로 한다.
if (self.searchType == nilself.searchType = @"BRANCH";
//위치 관리자를 초기화한다.
self.locationManager = [[[CLLocationManager allocinit]autorelease];
//딜리게이트는 self 설정후 하단에서 딜리게이트 구현.
self.locationManager.delegate = self;
//측정방법은 가장 좋게.
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
//2000m 이상 위치가 변경되면 노티를 .
self.locationManager.distanceFilter = 2000.0f
    [self.locationManager startUpdatingLocation]; //현재위치 가져오기 시작~
//지도 뷰를 만든다.
//뷰의 크기만큼 지도를 채운다.
mapView = [[MKMapView allocinitWithFrame: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.050.0)]; toolbar.barStyle = UIBarStyleBlackTranslucent; //툴바스타일은 까만 투명색
// 영역 잡아주는 버튼아이템왼쪽에 빈 영역 두고오른쪽으로 버튼들을 배치하기위함.
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]
   initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
   target:nil action:nil];
 
//이미지 커스텀 버튼.
UIBarButtonItem *hereBtn = [[UIBarButtonItem alloc]
   initWithCustomView:[selfcreateCustomImageButtonWithNormalImgNm:@"here.png"
   andTouchImg:@"here_pressed.png" andWidth:40.0f andHeight:40.0f
   andSEL:@selector(setSearchTypeToHere)]]; //현위치
UIBarButtonItem *branchBtn = [[UIBarButtonItem alloc]
   initWithCustomView:[selfcreateCustomImageButtonWithNormalImgNm:@"atm_btn.png" 
   andTouchImg:@"atm_btn_pressed.png" andWidth:40.0f andHeight:40.0f 
   andSEL:@selector(setSearchTypeToATM)]]; //ATM검색
UIBarButtonItem *atmBtn = [[UIBarButtonItem alloc]
   initWithCustomView:[selfcreateCustomImageButtonWithNormalImgNm:@"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.0480.0f/2.0)]; 
[self.view addSubview:spinner]; //스피너를 뷰에 추가하고 필요시에 start
//geoCoder 라벨 셋팅. '서울시 송파구 신천동따위를 툴바에 표시한다
geoLabel = [[UILabel alloc
initWithFrame:CGRectMake(5.0, heightPos - 45.0f160.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 = [NSMutableStringstringWithString: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:
[NSStringstringWithFormat:@"%f",location.coordinate.latitude]
strFrom:@"."strTo:@""];
NSString *lng = [self replaceStrSource:
[NSStringstringWithFormat:@"%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 = [[BranchMapGetDataActionalloc
initWithSearchType:sType andReqLat:lat andReqLng:lngandReqRange: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.coordinateautorelease];
    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 = [UIButtonbuttonWithType: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 = [[alertMessagestringByAppendingString:@"\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 allocinitWithTitle: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,20002000);
MKCoordinateRegion adjustedRegion = [mapViewregionThatFits: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:nilotherButtonTitles:@"확인",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.searchTypeisEqualToString:@""]){
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 = [NSURLConnectionsendSynchronousRequest:request
 returningResponse:&response error:&error];
//받아온 데이터 파싱.
NSString *str = [[NSString allocinitWithData: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 allocinit]; //리턴할배열 준비.
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 allocinit];
// 셋팅.
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.00.0 }, { 0.00.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 = [[UIAlertViewalloc]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)
twitter : @mokorean


* 추가  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 ///////////////