오버레이 공통

오버레이는 지리적 정보를 시각적으로 나타내는 요소로, 개발자가 지도 위에 자유롭게 배치할 수 있습니다. 네이버 지도 SDK는 마커, 정보 창, 셰이프 등 다양한 유형의 오버레이를 제공합니다. 자세한 내용은 각 오버레이 설명을 참고하십시오.

추가 및 삭제

객체 생성

위치 오버레이를 제외한 모든 오버레이는 일반적인 클래스 객체처럼 생성할 수 있습니다.

다음은 마커 객체를 생성하는 예제입니다.

let marker = NMFMarker()

Swift

let marker = NMFMarker()

Objective-C

NMFMarker *marker = [NMFMarker new];

위치 오버레이 객체는 NMFMapViewlocationOverlay 속성을 사용해 가져올 수 있습니다.

다음은 위치 오버레이 객체를 가져오는 예제입니다.

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;

UIView를 사용한 NMFOverlayImage

비트맵 이미지 외에도 커스텀 뷰를 사용한 NMFOverlayImage 객체를 생성할 수 있습니다. NMFOverlayImage로 만들 커스텀 뷰에 NMFOverlayImageDataSource 프로토콜을 구현하여 사용할 NMFOverlay 객체의 dataSource로 등록하면 됩니다.

메모리 관리

NMFOverlayImage 객체는 비트맵을 다루므로 사용 시 메모리 관리에 유의해야 합니다. 여러 오버레이가 같은 이미지를 사용할 경우 NMFOverlayImage 인스턴스를 하나만 만들어 사용해야 합니다.

다음은 marker1marker2는 같은 비트맵을 공유하지만, marker3marker4는 동일한 비트맵을 중복해서 사용하는 잘못된 예제입니다.

// 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 속성을 사용하면 겹침 우선순위를 지정할 수 있습니다. globalZIndex0 이상이면 심벌 위에, 0 미만이면 심벌 아래에 그려집니다. 단, globalZIndex가 아무리 작더라도 지도 배경보다는 위에 그려집니다. 각 오버레이 유형별 globalZIndex 기본값은 다음과 같습니다.

  • 정보 창: 400000
  • 위치 오버레이: 300000
  • 마커: 200000
  • 화살표 경로 오버레이: 100000
  • (지도 심벌)
  • 경로선: -100000
  • 셰이프(폴리곤, 폴리라인, 서클): -200000
  • 지상 오버레이: -300000
  • (지도 배경)

예를 들어 경로선은 기본적으로 셰이프의 위, 지도 심벌의 아래에 그려집니다. 그러나 경로선의 globalZIndex250000으로 지정하면 마커의 위, 위치 오버레이의 아래에 그려집니다.

다음은 세 오버레이의 겹침 우선순위를 폴리곤 -> 경로선 -> 마커 순으로 지정하는 예제입니다.

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-index를 지정한 모습

보조 Z 인덱스

zIndex 속성을 사용하면 globalZIndex가 동일한 오버레이간의 겹침 우선순위를 지정할 수 있습니다. 즉, 두 오버레이의 globalZIndex가 동일하다면 zIndex가 큰 오버레이가 작은 오버레이를 덮습니다. 예를 들어 마커의 zIndex1로 지정하고, 지상 오버레이의 zIndex2로 지정했다 하더라도 마커의 기본 globalZIndex200000, 지상 오버레이의 기본 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;

마커의 z-index를 지정한 모습

노출 제어

모든 오버레이에는 가시성, 최소 및 최대 줌 레벨에 대한 속성이 있습니다. 오버레이를 일시적으로 또는 특정한 상황에만 숨기고자 할 경우 mapView 속성을 변경하는 것보다 이러한 속성을 사용하면 효율적입니다.

가시성

hidden 속성을 YES로 지정하면 오버레이가 화면에서 숨겨집니다. 그러나 오버레이와 지도 간의 관계는 여전히 유지됩니다.

다음은 오버레이를 숨기는 예제입니다.

overlay.hidden = true

Swift

overlay.hidden = true

Objective-C

overlay.hidden = YES;

최소 및 최대 줌 레벨

minZoommaxZoom 속성을 이용하면 특정 줌 레벨에서만 오버레이가 나타나도록 지정할 수도 있습니다. 카메라의 줌 레벨이 minZoommaxZoom 범위를 벗어나면 오버레이가 숨겨집니다.

다음은 오버레이가 12~16 레벨 사이에서만 나타나도록 지정하는 예제입니다.

overlay.minZoom = 12
overlay.maxZoom = 16

Swift

overlay.minZoom = 12
overlay.maxZoom = 16

Objective-C

overlay.minZoom = 12;
overlay.maxZoom = 16;

이벤트

모든 오버레이는 터치 이벤트를 받고 소비할 수 있습니다. 오버레이에 대한 터치 이벤트는 지도가 아닌 각 오버레이에 지정해야 합니다.

터치 이벤트

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가 반환된 후 지도의 -didTapMapView:LatLng: 델리게이트 메서드가 호출됩니다. 반대로 touchHandlerYES를 반환할 경우 오버레이가 이벤트를 소비한 것으로 간주되어 지도의 -didTapMapView:LatLng:는 호출되지 않습니다.

다음은 marker1은 터치 이벤트를 전파하고 marker2는 소비하도록 하는 예제입니다. marker1이 탭될 때에는 "마커 터치됨" 로그와 "지도 터치됨" 로그가 모두 노출되지만, marker2가 탭될 때에는 "마커 2 터치됨" 로그만 노출됩니다.

marker1.touchHandler = { (overlay) -> Bool in
    print("마커 1 터치됨")
    // 이벤트 전파
    return false
}

marker2.touchHandler = { (overlay) -> Bool in
    print("마커 2 터치됨")
    return true
}

func didTapMapView(_ point: CGPoint, latLng latlng: NMGLatLng) {
    print("지도 터치됨")
}

Swift

marker1.touchHandler = { (overlay) -> Bool in
    print("마커 1 터치됨")
    // 이벤트 전파
    return false
}

marker2.touchHandler = { (overlay) -> Bool in
    print("마커 2 터치됨")
    return true
}

func didTapMapView(_ point: CGPoint, latLng latlng: NMGLatLng) {
    print("지도 터치됨")
}

Objective-C

marker1.touchHandler =  ^BOOL(NMFOverlay *overlay) {
    NSLog(@"마커 1 터치됨");
    // 이벤트 전파
    return NO;
};

marker2.touchHandler =  ^BOOL(NMFOverlay *overlay) {
    NSLog(@"마커 1 터치됨");
    // 이벤트 소비
    return YES;
};

- (void)didTapMapView:(CGPoint)point LatLng:(NMGLatLng*)latlng {
    NSLog(@"지도 터치됨");
}

오버레이에서 이벤트가 전파되는 곳은 오직 지도뿐입니다. 즉, 이벤트가 발생한 오버레이의 아래에 다른 오버레이나 심벌이 겹쳐져 있더라도 이 오버레이나 심벌로는 이벤트가 전파되지 않습니다.

userInfo

userInfo 속성을 사용하면 오버레이에 추가적인 정보를 지정할 수 있습니다. 이 속성은 터치 이벤트 리스너와 결합해 사용하면 특히 유용합니다. touchHandler에서 userInfo에 저장된 정보를 확인해 어떤 오버레이가 탭되었는지를 식별할 수 있으므로 여러 오버레이가 터치 핸들러를 공유하도록 구현할 수 있습니다.

다음은 marker1marker2userInfo를 추가하고 터치 핸들러를 공유하는 예제입니다.

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;

results matching ""

    No results matching ""