카메라 이동
카메라는 API 호출, 사용자의 제스처 등 다양한 방법으로 움직일 수 있습니다. API를 호출해 카메라를 움직이려면 NMFCameraUpdate
객체와 -moveCamera:
메서드를 사용합니다. NMFCameraUpdate
의 다양한 속성을 사용해 카메라의 위치, 애니메이션, 콜백 등을 지정할 수 있습니다.
API 호출로 카메라 이동하기
카메라를 움직이려면 먼저 카메라를 어떻게 움직일지를 나타내는 NMFCameraUpdate
객체를 생성해야 합니다. NMFCameraUpdate
는 카메라를 이동할 위치, 방법 등을 정의하는 클래스입니다. NMFCameraUpdate
객체는 오직 팩토리 메서드를 이용해서 생성할 수 있으며, NMFCameraUpdate
클래스에 다양한 팩토리 메서드가 정의되어 있습니다. 다음은 몇 가지 팩토리 메서드에 대한 설명입니다.
+cameraUpdateWithPosition:
cameraUpdateWithPosition:): 카메라의 위치를 지정한 CameraPosition으로 움직입니다.+cameraUpdateWithScrollTo:
cameraUpdateWithScrollTo:): 카메라의 대상 지점을 지정한 좌표로 변경합니다.+cameraUpdateWithScrollBy:
cameraUpdateWithScrollBy:): 카메라의 대상 지점을 지정한 포인트만큼 상하좌우로 이동합니다.+cameraUpdateWithZoomTo:
cameraUpdateWithZoomTo:): 카메라의 줌 레벨을 지정한 값으로 변경합니다.+cameraUpdateWithFitBounds:
cameraUpdateWithFitBounds:): 영역이 온전히 보이는 좌표와 최대 줌 레벨로 카메라의 위치를 변경합니다.
생성한 객체를 파라미터로 삼아 NMFMapView
의 -moveCamera:
를 호출하면 카메라가 움직입니다.
다음은 대상 지점을 (37.5666102, 126.9783881)
로 변경하도록 지정하는 NMFCameraUpdate
객체를 만들고 -moveCamera:
를 호출해 카메라를 움직이는 예제입니다.
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
mapView.moveCamera(cameraUpdate)
Swift
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
mapView.moveCamera(cameraUpdate)
Objective-C
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:NMGLatLngMake(37.5666102, 126.9783881)];
[self.mapView moveCamera:cameraUpdate];
애니메이션
생성한 NMFCameraUpdate
객체를 이용해 -moveCamera:
를 호출하기 전에 NMFCameraUpdate.animation
속성을 지정하면 카메라 이동에 애니메이션을 적용할 수 있습니다. 애니메이션이 지정되면 일정한 시간 동안 카메라가 목표 지점까지 서서히 움직입니다.
애니메이션 유형
NMFCameraUpdate.animation
을 사용하면 카메라 이동에 애니메이션을 적용할 수 있습니다. animation
은 애니메이션의 유형을 의미하는 NMFCameraUpdateAnimation
객체를 파라미터로 받습니다. NMFCameraUpdateAnimation
열거형에 나열되어 있는 애니메이션 유형은 다음과 같습니다.
None
: 애니메이션 없이 이동합니다. 기본값입니다.Linear
: 일정한 속도로 이동합니다.EaseIn
: 부드럽게 가속하며 이동합니다. 가까운 거리를 이동할 때 적합합니다.EaseOut
: 부드럽게 감속하며 이동합니다. 가까운 거리를 이동할 때 적합합니다.Fly
: 부드럽게 축소됐다가 확대되며 이동합니다. 먼 거리를 이동할 때 적합합니다.
다음은 카메라 이동에 EaseIn
애니메이션을 적용하는 예제입니다.
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .easeIn
mapView.moveCamera(cameraUpdate)
Swift
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .easeIn
mapView.moveCamera(cameraUpdate)
Objective-C
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:NMGLatLngMake(37.5666102, 126.9783881)];
cameraUpdate.animation = NMFCameraUpdateAnimationEaseIn
[self.mapView moveCamera:cameraUpdate];
지속 시간
NMFCameraUpdate
의 animationDuration
파라미터를 지정하면 애니메이션의 지속 시간을 변경할 수 있습니다. 단, 애니메이션의 유형이 NMFCameraUpdateAnimationNone
일 경우 시간은 무시됩니다. 또한 animationDuration
이 0
일 경우 애니메이션의 유형을 지정하더라도 애니메이션 없이 이동합니다.
다음은 카메라 이동에 Fly
애니메이션을 적용하고 지속 시간을 1초로 지정하는 예제입니다.
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .fly
cameraUpdate.animationDuration = 1
mapView.moveCamera(cameraUpdate)
Swift
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .fly
cameraUpdate.animationDuration = 1
mapView.moveCamera(cameraUpdate)
Objective-C
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:NMGLatLngMake(37.5666102, 126.9783881)];
cameraUpdate.animation = NMFCameraUpdateAnimationFly;
cameraUpdate.animationDuration = 1;
[self.mapView moveCamera:cameraUpdate];
지속 시간을 지정하지 않으면 지도의 기본 애니메이션 시간이 적용됩니다. 기본 애니메이션 시간은 NMFMapView
의 animationDuration
속성을 사용해 변경할 수 있습니다.
다음은 지도의 기본 애니메이션 시간을 500밀리초로 변경하는 예제입니다.
mapView.animationDuration = 0.5
Swift
mapView.animationDuration = 0.5
Objective-C
mapView.animationDuration = 0.5f;
애니메이션 취소
진행 중인 카메라 이동 애니메이션은 다양한 이유로 취소되거나 취소할 수 있습니다. NMFMapView
의 -moveCamera:
를 호출해 새로운 카메라 이동을 시작하면 이전에 진행 중이던 카메라 이동이 자동으로 취소됩니다.
다음은 지속 시간이 2초인 카메라 애니메이션을 시작하고 1초 후 다른 카메라 이동을 시작해 기존 카메라 애니메이션을 취소하는 예제입니다.
let coord1 = NMGLatLng(lat: 37.5666102, lng: 126.9783881);
let coord2 = NMGLatLng(lat: 37.57000, lng: 126.97618);
// 2초간 카메라 애니메이션
let cameraUpdate = NMFCameraUpdate(scrollTo: coord1)
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
mapView.moveCamera(cameraUpdate)
// 1초 후 다른 카메라 이동이 시작되며 기존 카메라 애니메이션이 취소됨
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let cameraUpdate = NMFCameraUpdate(scrollTo: coord2)
cameraUpdate.animation = .easeIn
mapView.moveCamera(cameraUpdate)
}
Swift
let coord1 = NMGLatLng(lat: 37.5666102, lng: 126.9783881);
let coord2 = NMGLatLng(lat: 37.57000, lng: 126.97618);
// 2초간 카메라 애니메이션
let cameraUpdate = NMFCameraUpdate(scrollTo: coord1)
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
mapView.moveCamera(cameraUpdate)
// 1초 후 다른 카메라 이동이 시작되며 기존 카메라 애니메이션이 취소됨
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let cameraUpdate = NMFCameraUpdate(scrollTo: coord2)
cameraUpdate.animation = .easeIn
mapView.moveCamera(cameraUpdate)
}
Objective-C
NMGLatLng *coord1 = NMGLatLngMake(37.5666102, 126.9783881);
NMGLatLng *coord2 = NMGLatLngMake(37.57000, 126.97618);
// 2초간 카메라 애니메이션
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:coord1];
cameraUpdate.animation = NMFCameraUpdateAnimationEaseIn;
cameraUpdate.animationDuration = 2;
[self.mapView moveCamera:cameraUpdate];
// 1초 후 다른 카메라 이동이 시작되며 기존 카메라 애니메이션이 취소됨
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:coord2];
cameraUpdate.animation = NMFCameraUpdateAnimationEaseIn;
[self.mapView moveCamera:cameraUpdate];
});
NMFMapView
의 -cancelTransitions
를 사용해서 명시적으로 카메라 애니메이션을 취소할 수도 있습니다. 호출하면 현재 진행 중인 카메라 애니메이션이 취소되고 카메라가 현재 위치에 멈춥니다. 스톱 제스처가 활성화되어 있다면 사용자가 지도를 탭했을 때에도 동일한 결과가 일어납니다.
다음은 -cancelTransitions
를 호출해 카메라 애니메이션을 취소하는 예제입니다.
// 2초간 카메라 애니메이션
let cameraUpdate = NMFCameraUpdate(scrollTo: coord1)
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
mapView.moveCamera(cameraUpdate)
// 1초 후 cancelTransitions()가 호출되며 기존 카메라 애니메이션이 취소됨
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
mapView.cancelTransitions()
}
Swift
// 2초간 카메라 애니메이션
let cameraUpdate = NMFCameraUpdate(scrollTo: coord1)
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
mapView.moveCamera(cameraUpdate)
// 1초 후 cancelTransitions()가 호출되며 기존 카메라 애니메이션이 취소됨
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
mapView.cancelTransitions()
}
Objective-C
// 2초간 카메라 애니메이션
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:coord1];
cameraUpdate.animation = NMFCameraUpdateAnimationEaseIn;
cameraUpdate.animationDuration = 2;
[self.mapView moveCamera:cameraUpdate];
// 1초 후 cancelTransitions()가 호출되며 기존 카메라 애니메이션이 취소됨
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.mapView cancelTransitions];
});
콜백과 이벤트
-moveCamera:
대신 -moveCamera:completion:
의 completion block을 사용하면 한 번의 카메라 이동에 대한 결과 콜백을 받을 수 있습니다. 또한 NMFMapView
에 델리게이트를 등록하면 지도에서 일어나는 모든 카메라 이동에 대한 이벤트를 받을 수 있습니다.
카메라 이동 콜백
-moveCamera:completion:
으로 콜백 블록을 구현하면 해당하는 NMFCameraUpdate
에 대한 카메라 이동이 완료됐거나 실패했을 때 콜백을 받을 수 있습니다. 카메라 이동에 애니메이션이 지정되어 있고 그 애니메이션이 취소되었을 경우 completion block의 isCancelled
파라미터가 YES
, 그 외의 경우에는 NO
로 지정됩니다. 즉, 카메라 이동에 애니메이션이 지정되지 않았으면 카메라 이동은 항상 성공적으로 완료된 것으로 간주됩니다.
다음은 카메라 이동 완료 및 실패 시 토스트를 표시하는 콜백을 지정하는 예제입니다.
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
mapView.moveCamera(cameraUpdate) { (isCancelled) in
if isCancelled {
print("카메라 이동 취소")
} else {
print("카메라 이동 완료")
}
}
Swift
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
mapView.moveCamera(cameraUpdate) { (isCancelled) in
if isCancelled {
print("카메라 이동 취소")
} else {
print("카메라 이동 완료")
}
}
Objective-C
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:NMGLatLngMake(37.5666102, 126.9783881)];
cameraUpdate.animation = NMFCameraUpdateAnimationEaseIn;
cameraUpdate.animationDuration = 2;
[self.mapView moveCamera:cameraUpdate completion:^(bool isCancelled) {
if (isCancelled) {
NSLog(@"카메라 이동 취소");
} else {
NSLog(@"카메라 이동 완료");
}
}];
카메라 변경 이벤트
어떤 이유에 의해서건 카메라가 움직이면 카메라 변경 이벤트가 발생합니다. NMFMapView
를 사용 중인 뷰 컨트롤러에 NMFMapViewCameraDelegate
프로토콜을 선언하고, -mapView:addCameraDelegate
로 등록하면 카메라 변경 이벤트를 받을 수 있습니다. 카메라의 위치가 변경되면 -mapView:cameraIsChangingByReason:
콜백 메서드가 호출됩니다.
카메라 이동이 시작되기 전 호출되는 -mapView:cameraWillChangeByReason:
과 이동이 완료된 후 호출되는 -mapView:cameraDidChangeByReason:
에는 reason
과 animated
파라미터가 전달됩니다. reason
은 이벤트를 발생시킨 카메라 이동의 원인입니다. NMFCameraUpdate.reason
을 이용해 카메라 이동의 원인을 지정할 수 있으며, 이벤트 리스너 내에서 이 값을 이용해 어떤 원인에 의해 발생한 이벤트인지 판단할 수 있습니다. 제스처, 컨트롤 등 네이버 지도 SDK의 내장 기능에 의해 카메라가 이동한 경우 미리 정의된 음숫값으로 설정되며, 개발자가 임의의 양숫값을 지정할 수도 있습니다. 미리 정의되어 있는 원인은 다음과 같습니다.
NMFMapChangedByDeveloper
: 개발자가 API를 호출해 카메라가 움직였음을 나타냅니다. 기본값입니다.NMFMapChangedByGesture
: 사용자의 제스처로 인해 카메라가 움직였음을 나타냅니다.NMFMapChangedByControl
: 사용자의 버튼 선택으로 인해 카메라가 움직였음을 나타냅니다.NMFMapChangedByLocation
: 위치 트래킹 기능으로 인해 카메라가 움직였음을 나타냅니다.
다음은 카메라 변경 시 원인을 로깅하는 델리게이트 메서드와 원인을 지정해 카메라를 이동시키는 예제입니다.
func mapViewRegionIsChanging(_ mapView: NMFMapView, byReason reason: Int) {
print("카메라 변경 - reason: \(reason)")
}
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
cameraUpdate.reason = 1000
mapView.moveCamera(cameraUpdate)
Swift
func mapViewRegionIsChanging(_ mapView: NMFMapView, byReason reason: Int) {
print("카메라 변경 - reason: \(reason)")
}
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.animation = .easeIn
cameraUpdate.animationDuration = 2
cameraUpdate.reason = 1000
mapView.moveCamera(cameraUpdate)
Objective-C
- (void)mapViewRegionIsChanging:(NMFMapView *)mapView byReason:(NSInteger)reason {
NSLog(@"카메라 변경 - reason : %ld", (long)reason);
}
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:NMGLatLngMake(37.5666102, 126.9783881)];
cameraUpdate.animation = NMFCameraUpdateAnimationEaseIn;
cameraUpdate.animationDuration = 2;
cameraUpdate.reason = 1000;
[self.mapView moveCamera:cameraUpdate];
카메라 대기 이벤트
카메라의 움직임이 끝나 대기 상태가 되면 카메라 대기 이벤트가 발생합니다. -mapViewCameraIdle:
메서드로 카메라 대기 이벤트를 받을 수 있습니다. 카메라가 대기 상태가 되면 -mapViewCameraIdle:
가 호출됩니다.
카메라는 다음과 같은 시점에 대기 상태가 된 것으로 간주되어 이벤트가 발생합니다.
- 카메라가 애니메이션 없이 움직일 때. 단, 사용자가 제스처로 지도를 움직이는 경우 제스처가 완전히 끝날때까지(
touchesEnded:
가 발생할 때까지)는 연속적인 이동으로 간주되어 이벤트가 발생하지 않습니다. - 카메라 애니메이션이 완료될 때. 단, 카메라 애니메이션이 진행 중일 때 새로운 애니메이션이 발생하거나, 기존 카메라 이동의
completion
으로 지정된 콜백 내에서 카메라 이동이 일어날 경우 연속적인 이동으로 간주되어 이벤트가 발생하지 않습니다. NMFMapView.cancelTransitions()
가 호출되어 카메라 애니메이션이 명시적으로 취소될 때.
다음은 카메라가 대기 상태가 되면 얼럿을 표시하는 예제입니다.
func mapViewCameraIdle(_ mapView: NMFMapView) {
let alert = UIAlertController(title: "카메라 움직임 종료",
message: nil,
preferredStyle: .alert)
present(alert, animated: true, completion: {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: {
alert.dismiss(animated: true, completion: nil)
})
})
}
Swift
func mapViewCameraIdle(_ mapView: NMFMapView) {
let alert = UIAlertController(title: "카메라 움직임 종료",
message: nil,
preferredStyle: .alert)
present(alert, animated: true, completion: {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: {
alert.dismiss(animated: true, completion: nil)
})
})
}
Objective-C
- (void)mapViewCameraIdle:(NMFMapView *)mapView {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"카메라 움직임 종료" message:nil preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alert animated:true completion:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[alert dismissViewControllerAnimated:YES completion:nil];
});
}];
}
피봇
NMFCameraUpdate.pivot
속성을 사용해 피봇 지점을 지정할 수 있습니다. 피봇 지점은 화면의 왼쪽 위를 (0, 0)
, 오른쪽 아래를 (1, 1)
로 삼는 비율값으로, 카메라를 움직일 때 기준점이 되는 화면상 지점을 의미합니다. 피봇 지점이 지정되면 이동, 줌 레벨 변경, 회전이 화면 중심이 아닌 해당 지점을 기준으로 이루어집니다.
예를 들어 피봇 지점을 (0.5, 0.8)
로 지정하고 카메라를 이동하면, 카메라 이동이 완료된 후 (0.5, 0.8)
지점의 좌표가 카메라 이동의 대상 지점과 일치하게 됩니다. 동일한 피봇 지점을 지정하고 지도를 확대하면 지도의 중심이 아닌 (0.5, 0.8)
지점을 기준으로 지도가 확대됩니다. 따라서 피봇 지점을 지정하면 카메라 이동이 완료된 후 카메라의 위치가 카메라 이동에서 지정했던 위치와 다를 수 있습니다.
피봇은 카메라가 위치해야 하는 화면상 지점을 지정한다는 측면에서는 콘텐츠 패딩과 동일한 역할을 합니다. 그러나 NMFMapView
의 속성인 콘텐츠 패딩과 달리 피봇은 NMFCameraUpdate
의 속성이므로 하나의 카메라 변경에만 적용되며, 지도 객체에 영구적으로 영향을 미치지 않습니다.
다음은 피봇 지점을 (0.5, 0.8)
로 지정하고 카메라를 이동시키는 예제입니다.
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.pivot = CGPoint(x: 0.5, y: 0.8)
mapView.moveCamera(cameraUpdate)
Swift
let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.5666102, lng: 126.9783881))
cameraUpdate.pivot = CGPoint(x: 0.5, y: 0.8)
mapView.moveCamera(cameraUpdate)
Objective-C
NMFCameraUpdate *cameraUpdate = [NMFCameraUpdate cameraUpdateWithScrollTo:NMGLatLngMake(37.5666102, 126.9783881)];
cameraUpdate.pivot = CGPointMake(0.5, 0.8);
[self.mapView moveCamera:cameraUpdate];
이동 제한
NMFMapView
의 속성을 지정하면 카메라가 이동할 수 있는 영역과 최소 및 최대 줌 레벨을 제한할 수 있습니다.
최소 및 최대 줌 레벨
minZoomLevel
및 maxZoomLevel
속성을 지정하면 카메라의 최소 및 최대 줌 레벨을 제한할 수 있습니다.
다음은 카메라의 줌 레벨을 5~18 범위로 제한하는 예제입니다.
mapView.minZoomLevel = 5.0
mapView.maxZoomLevel = 18.0
Swift
mapView.minZoomLevel = 5.0
mapView.maxZoomLevel = 18.0
Objective-C
self.mapView.minZoomLevel = 5.0;
self.mapView.maxZoomLevel = 18.0;
카메라 영역 제한
extent
속성을 지정하면 카메라의 대상 지점을 영역 내로 제한할 수 있습니다. 카메라가 제한 영역을 벗어나도록 API를 호출하더라도 대상 지점이 영역 내로 조정됩니다.
다음은 카메라의 대상 지점을 한반도 인근으로 제한하는 예제입니다.
mapView.extent = NMGLatLngBounds(southWestLat: 31.43, southWestLng: 122.37, northEastLat: 44.35, northEastLng: 132)
Swift
mapView.extent = NMGLatLngBounds(southWestLat: 31.43, southWestLng: 122.37, northEastLat: 44.35, northEastLng: 132)
Objective-C
self.mapView.extent = NMGLatLngBoundsMake(31.43, 122.37, 44.35, 132);
카메라 영역을 제한할 때 최소 줌 레벨도 함께 제한하는 것이 좋습니다. 그렇지 않으면 지도가 축소되었을 때 제한 영역이 너무 작게 나타날 수 있습니다.