http://lomohome.com/321 by Geunwon,Mo (mokorean@gmail.com) Android 의 MapView (Google API) 정리하기에이은 ios (iphone,ipod touch) 의 MKMapView 정리하기. 저번엔안드로이드용위치기반지점찾기 (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]; - 다음페이지에 viewdidload 메소드계속
// 이미지커스텀버튼. 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 - BranchMapViewController.m 끝.
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]; - 다음페이지에 getdata 메소드계속
// 마커배열준비. // 받아온스트링을세미콜론으로잘라서배열로넣어버린다. 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 { - 다음페이지에 getdata 메소드계속
// 검색결과가없을때오류표시. 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 - BranchMapGetDataAction.m 끝.
5. 실행스크린샷. 최초실행하면안내문구가나오고, 현재위치를표시하며, 현재위치기준으로지점을찾아서마커를주루룩찍어낸다. 현재위치마커를누르면 현재위치 라는타이틀이나타난다. 지점 /ATM 마커를누르면간단한정보가나온다.
마커의간단한정보에서오른쪽의버튼을누르면상세정보가나오고, 창을닫거나, 전화를걸수있다. 현위치말고도원하는지역으로가서하단의아이콘을누르면해당지역을기준으로지점 /ATM 정보가나오게된다.
6. 해결하지못한부분 - MKMapview 의지도화면에특정지점을눌러서뭔가액션을해주고싶어 MKMapview 를상속하여커스텀맵뷰를구현했는데터치이벤트에서오류작렬 그리고줌인줌아웃시에오류가난다.. ㅠ - ios 4 부터는스테이터스바에위치서비스사용아이콘이나오는데분명 LocationManager 를 stop 시켜주었는데도아이콘이계속떠있다. Tweeter 어플등을보면현위치를가져오고난뒤에는아이콘이사라지는것같은데.. - 3G 네트워크등. 인터넷상태가불안정할때처리에오래걸리는문제. 데이터를받아오는순간에프로그램이정지된것처럼보인다. ios 도스레드를돌려서백그라운드로돌려야하나? 이건다음버전에서고민. 2010.11.17 모근원 (Geunwon,Mo) mokorean@gmail.com twitter : @mokorean http://lomohome.com