프로젝트의 프레임웍에서 마우스 오른쪽버튼 (또는 옵션클릭)을 하여 프레임웍을 추가해준다.
// 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 이 멀쩡히 돌아간다.
이제 실제 지도를 구현해본다. 이번 어플에서는 크게 다음과 같이 네개의 뷰가 겹쳐져 있다.
// 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;
//한번 위치를 잡으면 <