오버레이 공통
오버레이는 지리적 정보를 시각적으로 나타내는 요소로, 개발자가 지도 위에 자유롭게 배치할 수 있습니다. 네이버 지도 SDK는 마커, 정보 창, 셰이프 등 다양한 유형의 오버레이를 제공합니다. 자세한 내용은 각 오버레이 설명을 참고하십시오.
추가 및 삭제
객체 생성
위치 오버레이를 제외한 모든 오버레이는 일반적인 클래스 객체처럼 생성할 수 있습니다.
다음은 마커 객체를 생성하는 예제입니다.
let marker = NMFMarker()
Swift
let marker = NMFMarker()
Objective-C
NMFMarker *marker = [NMFMarker new];
위치 오버레이 객체는 NMFMapView
의 locationOverlay
속성을 사용해 가져올 수 있습니다.
다음은 위치 오버레이 객체를 가져오는 예제입니다.
let locationOverlay = mapView.locationOverlay
Swift
let locationOverlay = mapView.locationOverlay
Objective-C
NMFLocationOverlay *locationOverlay = self.mapView.locationOverlay;
오버레이 추가
위치 오버레이를 제외한 모든 오버레이는 mapView
속성을 지정해 오버레이를 지도에 추가할 수 있습니다. 위치 오버레이를 지도에 추가하는 방법은 위치 오버레이 문서를 참고하십시오.
다음은 마커를 지도에 추가하는 예제입니다.
marker.mapView = mapView
Swift
marker.mapView = mapView
Objective-C
marker.mapView = self.mapView;
정보 창은 mapView
속성 외에도 -openWithMarker:
메서드를 사용해 지도 또는 마커를 대상으로 열 수 있습니다.
다음은 마커를 대상으로 정보 창을 여는 예제입니다.
infoWindow.open(with: marker)
Swift
infoWindow.open(with: marker)
Objective-C
[infoWindow openWithMarker:marker];
mapView
는 오버레이의 속성이므로, 하나의 오버레이는 동시에 두 개 이상의 지도에 나타날 수 없습니다. 이미 지도에 나타나 있는 오버레이에 새로이 mapView
속성을 지정하면 오버레이는 기존의 지도에서 사라지고 새로운 지도에 나타납니다.
오버레이 제거
위치 오버레이를 제외한 모든 오버레이는 mapView
속성을 nil
로 지정해 지도에서 제거할 수 있습니다. 위치 오버레이를 지도에서 삭제하는 방법은 위치 오버레이 문서를 참고하십시오.
다음은 마커를 지도에서 제거하는 예제입니다.
marker.mapView = nil
Swift
marker.mapView = nil
Objective-C
marker.mapView = nil;
정보 창은 mapView
속성 외에도 -close
메서드를 사용해 닫을 수 있습니다.
다음은 정보 창을 닫는 예제입니다.
infoWindow.close();
Swift
infoWindow.close();
Objective-C
[infoWindow close];
멀티스레딩
오버레이 객체는 아무 스레드에서나 생성할 수 있습니다. 그러나 오버레이의 속성은 스레드 안전성이 보장되지 않으므로 여러 스레드에서 동시에 접근해서는 안됩니다. 특히 지도에 추가된 오버레이의 속성은 메인 스레드에서만 접근해야 하며, 그렇지 않으면 NSObjectInaccessibleException
이 발생합니다. 단, 오버레이가 지도에 추가되지 않았다면 다른 스레드에서 오버레이의 속성에 접근해도 예외가 발생하지 않습니다.
따라서 대량의 오버레이를 다룰 경우 객체를 생성하고 초기 옵션을 지정하는 작업은 백그라운드 스레드에서 수행하고 지도에 추가하는 작업만을 메인 스레드에서 수행하면 메인 스레드를 효율적으로 사용할 수 있습니다. 다음은 1000개의 마커를 백그라운드 스레드에서 생성하고 속성을 지정한 후 메인 스레드에서 지도에 추가하는 예제입니다.
DispatchQueue.global(qos: .default).async {
// 백그라운드 스레드
var markers = [NMFMarker]()
for index in 1...1000 {
let marker = NMFMarker(position: ...)
marker.iconImage = ...
marker.captionText = ...
markers.append(marker)
}
DispatchQueue.main.async { [weak self] in
// 메인 스레드
for marker in markers {
marker.mapView = self?.mapView
}
}
}
Swift
DispatchQueue.global(qos: .default).async {
// 백그라운드 스레드
var markers = [NMFMarker]()
for index in 1...1000 {
let marker = NMFMarker(position: ...)
marker.iconImage = ...
marker.captionText = ...
markers.append(marker)
}
DispatchQueue.main.async { [weak self] in
// 메인 스레드
for marker in markers {
marker.mapView = self?.mapView
}
}
}
Objective-C
__block ViewController *weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 백그라운드 스레드
NSMutableArray<NMFMarker *> *markers = [NSMutableArray array];
for (NSInteger i = 0; i < 1000; i++) {
NMFMarker *marker = [NMFMarker markerWithPosition:...];
marker.iconImage = ...;
marker.captionText = ...;
[markers addObject:marker];
}
dispatch_async(dispatch_get_main_queue(), ^(){
// 메인 스레드
for (NMFMarker *marker in markers) {
marker.mapView = weakSelf.mapView;
}
});
});
비트맵 사용
마커, 정보 창, 위치 오버레이, 지상 오버레이, 경로선 오버레이 등 많은 오버레이는 비트맵 이미지를 사용합니다. 오버레이에서 비트맵 이미지를 사용하려면 NMFOverlayImage
객체를 만들어야 합니다.
NMFOverlayImage 객체 생성
NMFOverlayImage
는 아이콘으로 사용될 수 있는 비트맵 이미지를 나타내는 불변 클래스입니다. NMFOverlayImage
클래스에 정의된 팩토리 메서드를 이용해 이미지 에셋, UIImage
, NSBundle
등으로부터 인스턴스를 생성할 수 있습니다. 다음은 몇 가지 팩토리 메서드에 대한 설명입니다.
+overlayImageWithName:
overlayImageWithName:): 지정된 이름의 이미지 에셋으로부터 객체를 생성합니다.+overlayImageWithImage:
overlayImageWithImage:):UIImage
로부터 객체를 생성합니다.+overlayImageWithName:inBundle:
overlayImageWithName:inBundle:): 지정된 번들 안에 있는 이미지 에셋에서 지정된 이름의 이미지를 찾아 객체를 생성합니다.
다음은 이미지 에셋 리소스로부터 NMFOverlayImage
객체를 생성하는 예제입니다.
let image = NMFOverlayImage(name: "marker_icon")
Swift
let image = NMFOverlayImage(name: "marker_icon")
Objective-C
NMFOverlayImage *image = [NMFOverlayImage overlayImageWithName:@"marker_icon"];
비트맵을 필요로 하는 오버레이의 속성에 NMFOverlayImage
객체를 지정하면 이미지가 반영됩니다. 자세한 내용은 각 오버레이 가이드를 참고하십시오.
다음은 마커의 아이콘을 지정하는 예제입니다.
let image = NMFOverlayImage(name: "marker_icon")
marker.iconImage = image
Swift
let image = NMFOverlayImage(name: "marker_icon")
marker.iconImage = image
Objective-C
NMFOverlayImage *image = [NMFOverlayImage overlayImageWithName:@"marker_icon"];
marker.iconImage = image;
메모리 관리
NMFOverlayImage
객체는 비트맵을 다루므로 사용 시 메모리 관리에 유의해야 합니다. 여러 오버레이가 같은 이미지를 사용할 경우 NMFOverlayImage
인스턴스를 하나만 만들어 사용해야 합니다.
다음은 marker1
과 marker2
는 같은 비트맵을 공유하지만, marker3
과 marker4
는 동일한 비트맵을 중복해서 사용하는 잘못된 예제입니다.
// Good: marker1, 2가 같은 비트맵을 공유
let image = NMFOverlayImage(name: "marker_icon")
marker1.iconImage = image
marker2.iconImage = image
// Bad: marker3, 4가 비트맵을 중복해서 사용
marker3.iconImage = NMFOverlayImage(name: "marker_icon")
marker4.iconImage = NMFOverlayImage(name: "marker_icon")
Swift
// Good: marker1, 2가 같은 비트맵을 공유
let image = NMFOverlayImage(name: "marker_icon")
marker1.iconImage = image
marker2.iconImage = image
// Bad: marker3, 4가 비트맵을 중복해서 사용
marker3.iconImage = NMFOverlayImage(name: "marker_icon")
marker4.iconImage = NMFOverlayImage(name: "marker_icon")
Objective-C
// Good: marker1, 2가 같은 비트맵을 공유
NMFOverlayImage *image = NMFOverlayImage overlayImageWithName:@"marker_icon"];
marker1.iconImage = image;
marker2.iconImage = image;
// Bad: marker3, 4가 비트맵을 중복해서 사용
marker3.iconImage = [NMFOverlayImage overlayImageWithName:@"marker_icon"];
marker4.iconImage = [NMFOverlayImage overlayImageWithName:@"marker_icon"];
단, UIImage
객체로부터 NMFOverlayImage
객체를 만들었다면, NMFOverlayImage
가 나타내는 UIImage
가 동일할 경우 인스턴스가 다르더라도 동일한 비트맵을 공유하게 됩니다.
다음은 marker1
, marker2
, marker3
, marker4
모두 동일한 비트맵을 공유하는 예제입니다.
// Good: marker1, 2가 같은 비트맵을 공유
let image = NMFOverlayImage(image: bitmap)
marker1.iconImage = image
marker2.iconImage = image
// OK: marker3, 4가 다른 NMFOverlayImage 객체를 사용하지만 참조하는 리소스가 같으므로 비트맵도 공유
marker3.iconImage = NMFOverlayImage(image: bitmap)
marker4.iconImage = NMFOverlayImage(image: bitmap)
Swift
// Good: marker1, 2가 같은 비트맵을 공유
let image = NMFOverlayImage(image: bitmap)
marker1.iconImage = image
marker2.iconImage = image
// OK: marker3, 4가 다른 NMFOverlayImage 객체를 사용하지만 참조하는 리소스가 같으므로 비트맵도 공유
marker3.iconImage = NMFOverlayImage(image: bitmap)
marker4.iconImage = NMFOverlayImage(image: bitmap)
Objective-C
// Good: marker1, 2가 같은 비트맵을 공유
NMFOverlayImage *image = [NMFOverlayImage overlayImageWithImage:bitmap];
marker1.iconImage = image;
marker2.iconImage = image;
// OK: marker3, 4가 다른 OverlayImage 객체를 사용하지만 참조하는 리소스가 같으므로 비트맵도 공유
marker3.iconImage = [NMFOverlayImage overlayImageWithImage:bitmap];
marker4.iconImage = [NMFOverlayImage overlayImageWithImage:bitmap];
지도가 감당할 수 없을 정도로 많은 비트맵이 사용되면 XCode console에 "Overlay image atlas overflow"라는 경고가 로깅되고 렌더링이 무척 느려집니다. 메모리 사용량이 더욱 증가해 시스템이 감당할 수 없을 정도가 되면 OOM(Out of memory)으로 프로세스가 종료될 수 있습니다.
겹침 우선순위
여러 오버레이가 화면에서 겹쳐지면 우선순위가 높은 오버레이가 낮은 오버레이를 덮습니다. 겹침 우선순위는 기본적으로 오버레이의 유형에 따라 지정되지만 변경할 수 있습니다.
전역 Z 인덱스
globalZIndex
속성을 사용하면 겹침 우선순위를 지정할 수 있습니다. globalZIndex
가 0
이상이면 심벌 위에, 0
미만이면 심벌 아래에 그려집니다. 단, globalZIndex
가 아무리 작더라도 지도 배경보다는 위에 그려집니다. 각 오버레이 유형별 globalZIndex
기본값은 다음과 같습니다.
- 정보 창:
400000
- 위치 오버레이:
300000
- 마커:
200000
- 화살표 경로 오버레이:
100000
- (지도 심벌)
- 경로선:
-100000
- 셰이프(폴리곤, 폴리라인, 서클):
-200000
- 지상 오버레이:
-300000
- (지도 배경)
예를 들어 경로선은 기본적으로 셰이프의 위, 지도 심벌의 아래에 그려집니다. 그러나 경로선의 globalZIndex
를 250000
으로 지정하면 마커의 위, 위치 오버레이의 아래에 그려집니다.
다음은 세 오버레이의 겹침 우선순위를 폴리곤 -> 경로선 -> 마커 순으로 지정하는 예제입니다.
polygon.globalZIndex = 150000
path.globalZIndex = 50000
marker.globalZIndex = -500000
Swift
polygon.globalZIndex = 150000
path.globalZIndex = 50000
marker.globalZIndex = -500000
Objective-C
polygon.globalZIndex = 150000;
path.globalZIndex = 50000;
marker.globalZIndex = -50000;
보조 Z 인덱스
zIndex
속성을 사용하면 globalZIndex
가 동일한 오버레이간의 겹침 우선순위를 지정할 수 있습니다. 즉, 두 오버레이의 globalZIndex
가 동일하다면 zIndex
가 큰 오버레이가 작은 오버레이를 덮습니다. 예를 들어 마커의 zIndex
를 1
로 지정하고, 지상 오버레이의 zIndex
를 2
로 지정했다 하더라도 마커의 기본 globalZIndex
는 200000
, 지상 오버레이의 기본 globalZIndex
는 -300000
이므로 마커가 지상 오버레이를 덮습니다. zIndex
의 기본값은 오버레이의 유형과 무관하게 0
이므로, 동일한 유형의 오버레이간의 우선순위만을 지정하고자 하는 경우 globalZIndex
보다 직관적으로 사용할 수 있습니다.
다음은 세 마커의 노출 순서를 노란색 -> 녹색 -> 파란색 순으로 지정하는 예제입니다.
yellowMarker.zIndex = 100
greenMarker.zIndex = 0
blueMarker.zIndex = -10
Swift
yellowMarker.zIndex = 100
greenMarker.zIndex = 0
blueMarker.zIndex = -10
Objective-C
yellowMarker.zIndex = 100;
greenMarker.zIndex = 0;
blueMarker.zIndex = -10;
노출 제어
모든 오버레이에는 가시성, 최소 및 최대 줌 레벨에 대한 속성이 있습니다. 오버레이를 일시적으로 또는 특정한 상황에만 숨기고자 할 경우 mapView
속성을 변경하는 것보다 이러한 속성을 사용하면 효율적입니다.
가시성
hidden
속성을 YES
로 지정하면 오버레이가 화면에서 숨겨집니다. 그러나 오버레이와 지도 간의 관계는 여전히 유지됩니다.
다음은 오버레이를 숨기는 예제입니다.
overlay.hidden = true
Swift
overlay.hidden = true
Objective-C
overlay.hidden = YES;
최소 및 최대 줌 레벨
minZoom
및 maxZoom
속성을 이용하면 특정 줌 레벨에서만 오버레이가 나타나도록 지정할 수도 있습니다. 카메라의 줌 레벨이 minZoom
과 maxZoom
범위를 벗어나면 오버레이가 숨겨집니다. isMinZoomInclusive
및 isMaxZoomInclusive
속성을 이용하면 최소/최대 줌 레벨에서 오버레이가 나타날지 여부를 지정할 수도 있습니다. 예를 들어 minZoom
이 15
이고 지도의 줌 레벨도 15
일 때, isMinZoomInclusive
가 YES
라면 오버레이가 나타나고 NO
라면 나타나지 않습니다.
다음은 오버레이가 12~16 레벨 사이에서만 나타나도록 지정하는 예제입니다.
overlay.minZoom = 12
overlay.isMinZoomInclusive = true
overlay.maxZoom = 16
overlay.isMaxZoomInclusive = false
Swift
overlay.minZoom = 12
overlay.isMinZoomInclusive = true
overlay.maxZoom = 16
overlay.isMaxZoomInclusive = false
Objective-C
overlay.minZoom = 12;
overlay.isMinZoomInclusive = YES;
overlay.maxZoom = 16;
overlay.isMaxZoomInclusive = NO;
이벤트
모든 오버레이는 터치 이벤트를 받고 소비할 수 있습니다. 오버레이에 대한 터치 이벤트는 지도가 아닌 각 오버레이에 지정해야 합니다.
터치 이벤트
touchHandler
속성에 overlayTouchHandler
타입을 구현하여 지정하면 오버레이에 대한 터치 이벤트를 받을 수 있습니다. 오버레이가 탭되면 touchHandler
에 구현된 block 메서드가 호출되며, 탭된 오버레이 객체가 파라미터로 전달됩니다.
다음은 오버레이가 탭될 때마다 "오버레이 터치됨" 로그가 출력되도록 터치 핸들러를 지정하는 예제입니다.
overlay.touchHandler = { (overlay: NMFOverlay) -> Bool in
print("오버레이 터치됨")
return true
}
Swift
overlay.touchHandler = { (overlay: NMFOverlay) -> Bool in
print("오버레이 터치됨")
return true
}
Objective-C
overlay.touchHandler = ^BOOL(NMFOverlay *overlay) {
NSLog(@"오버레이 터치됨");
return YES;
};
하나의 오버레이에는 하나의 터치 이벤트 리스너만 지정할 수 있습니다. 터치 이벤트 리스너를 해제하려면 touchHandler
속성에 nil
을 지정합니다.
다음은 오버레이의 터치 핸들러를 해제하는 예제입니다.
overlay.touchHandler = nil
Swift
overlay.touchHandler = nil
Objective-C
overlay.touchHandler = nil;
터치 핸들러는 이벤트를 받고자 하는 모든 오버레이에 추가해야 합니다. 만약 오버레이에 터치 핸들러가 등록되어 있지 않다면 그 오버레이는 탭됐을 때 무시됩니다. 즉, 두 오버레이가 겹쳐져 있고 위에 있는 오버레이에 터치 핸들러가 등록되어 있지 않다면 아래에 있는 오버레이가 이벤트를 받습니다. 아래에 오버레이가 없거나 터치 핸들러가 등록되어 있지 않다면 지도가 탭된 것으로 간주됩니다.
이벤트 전파 및 소비
오버레이의 터치 이벤트는 지도로 전파될 수 있습니다. 이벤트를 지도로 전파하려면 touchHandler
의 값으로 NO
를 반환하도록 구현해야 합니다. 그렇게 하면 touchHandler
가 반환된 후 지도의 -mapView:didTapMap:point
메서드가 호출됩니다. 반대로 touchHandler
가 YES
를 반환할 경우 오버레이가 이벤트를 소비한 것으로 간주되어 지도의 -mapView:didTapMap:point
는 호출되지 않습니다.
다음은 marker1
은 터치 이벤트를 전파하고 marker2
는 소비하도록 하는 예제입니다. marker1
이 탭될 때에는 "마커 터치됨" 로그와 "지도 터치됨" 로그가 모두 노출되지만, marker2
가 탭될 때에는 "마커 2 터치됨" 로그만 노출됩니다.
marker1.touchHandler = { (overlay) -> Bool in
print("마커 1 터치됨")
// 이벤트 전파
return false
}
marker2.touchHandler = { (overlay) -> Bool in
print("마커 2 터치됨")
return true
}
func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) {
print("지도 터치됨")
}
Swift
marker1.touchHandler = { (overlay) -> Bool in
print("마커 1 터치됨")
// 이벤트 전파
return false
}
marker2.touchHandler = { (overlay) -> Bool in
print("마커 2 터치됨")
return true
}
func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) {
print("지도 터치됨")
}
Objective-C
marker1.touchHandler = ^BOOL(NMFOverlay *overlay) {
NSLog(@"마커 1 터치됨");
// 이벤트 전파
return NO;
};
marker2.touchHandler = ^BOOL(NMFOverlay *overlay) {
NSLog(@"마커 1 터치됨");
// 이벤트 소비
return YES;
};
- (void)mapView:(NMFMapView *)mapView didTapMap:(NMGLatLng *)latlng point:(CGPoint)point {
NSLog(@"지도 터치됨");
}
오버레이에서 이벤트가 전파되는 곳은 오직 지도뿐입니다. 즉, 이벤트가 발생한 오버레이의 아래에 다른 오버레이나 심벌이 겹쳐져 있더라도 이 오버레이나 심벌로는 이벤트가 전파되지 않습니다.
userInfo
userInfo
속성을 사용하면 오버레이에 추가적인 정보를 지정할 수 있습니다. 이 속성은 터치 이벤트 리스너와 결합해 사용하면 특히 유용합니다. touchHandler
에서 userInfo
에 저장된 정보를 확인해 어떤 오버레이가 탭되었는지를 식별할 수 있으므로 여러 오버레이가 터치 핸들러를 공유하도록 구현할 수 있습니다.
다음은 marker1
과 marker2
에 userInfo
를 추가하고 터치 핸들러를 공유하는 예제입니다.
let handler: overlayTouchHandler = { (overlay) -> Bool in
print("마커 \(overlay.userInfo["tag"] ?? "tag") 터치됨")
return false
}
marker1.userInfo = ["tag": 1]
marker2.userInfo = ["tag": 2]
marker1.touchHandler = handler
marker2.touchHandler = handler
Swift
let handler: overlayTouchHandler = { (overlay) -> Bool in
print("마커 \(overlay.userInfo["tag"] ?? "tag") 터치됨")
return false
}
marker1.userInfo = ["tag": 1]
marker2.userInfo = ["tag": 2]
marker1.touchHandler = handler
marker2.touchHandler = handler
Objective-C
overlayTouchHandler handler = ^BOOL(NMFOverlay *overlay) {
NSLog(@"마커 %@ 터치됨", overlay.userInfo[@"tag"]);
return NO;
};
marker1.userInfo = @{@"tag":@1};
marker2.userInfo = @{@"tag":@2};
marker1.touchHandler = handler;
marker2.touchHandler = handler;