EventHandling
KakaoMapsSDK 에서는 지도 내에서 발생하는 다양한 동작에 대해서 이벤트 처리를 위한 EventDelegate, 혹은 Event handler를 제공합니다.
EventDelegate
EventDelegate를 통해 지도 내에서 발생하는 이벤트를 일괄적으로 delegate protocol을 통해 핸들링 할 수 있습니다. delegate는 필수적인것을 제외하고는 대부분이 optional로 구성되어 있으므로, 필요한 함수를 구현하여 사용할 수 있습니다.
아래 예제는 KakaoMapEventDelegate를 이용하여, 탭한 지점에 poi를 표시하는 간략한 예제입니다.
// KakaoMapEventDelgate를 이용하여 terrain이 탭 되었을 때 action을 정의한다.
class PoiClickServiceSample: APISampleBaseViewController, KakaoMapEventDelegate {
// terrain이 탭되면 호출.
func terrainDidTapped(kakaoMap: KakaoMap, position: MapPoint) {
let mapView: KakaoMap = mapController?.getView("mapview") as! KakaoMap
let manager = mapView.getLabelManager()
let layer = manager.getLabelLayer(layerID: "example_click_service")
let option = PoiOptions(styleID: "label_default_style")
option.clickable = true
let poi = layer?.addPoi(option: option, at: position)
poi?.show()
}
}
Event Handler
일부 이벤트는 Delegate외에 객체마다 개별 이벤트를 전달받을 수 있는 Event Handler를 제공합니다. 객체마다 별도로 동작하는 EventHandler를 추가하여, 생성한 객체마다 다른 이벤트 핸들링을 정의할 수 있습니다. 아래 예제는 Poi를 생성하여, 해당 Poi에 이벤트 핸들러를 추가하여 Poi를 클릭했을 때의 동작을 정의하는 예제입니다.
// POI를 생성한다.
func createPois() {
let view = mapController?.getView("mapview") as! KakaoMap
let manager = view.getLabelManager()
let layer = manager.getLabelLayer(layerID: "PoiLayer") // 생성한 POI를 추가할 레이어를 가져온다.
let poiOption = PoiOptions(styleID: "customStyle1") // 생성할 POI의 Option을 지정하기 위한 자료를 담는 클래스를 생성. 사용할 스타일의 ID를 지정한다.
poiOption.rank = 0
poiOption.clickable = true // clickable 옵션을 true로 설정한다. default는 false로 설정되어있다.
let poi1 = layer?.addPoi(option: poiOption, at: MapPoint(longitude: 127.108678, latitude: 37.402001)) //레이어에 지정한 옵션 및 위치로 POI를 추가한다.
let _ = poi1?.addPoiTappedEventHandler(target: self, handler: SimplePOI.poiTappedHandler) // poi tap event handler를 추가한다.
poi1?.show()
}
// POI 탭 이벤트가 발생하고, 표시하고 있던 Poi를 숨긴다.
func poiTappedHandler(_ param: PoiInteractionEventParam) {
param.poiItem.hide()
}
Callback
KakaoMapsSDK 에서는 지도 위에 Label, Shape, Route 종류의 객체를 지도 위에 추가/삭제할 때 작업이 완료된 시점에 호출할 수 있는 callback 파라미터를 제공합니다. 또한, 카메라의 이동이 완료되었을 때 호출할 수 있는 callback 파라미터를 제공합니다. 객체를 추가하거나 삭제할 때 , 혹은 카메라를 이동할 때 파라미터에 콜백 함수를 함께 전달하여 작업이 완료된 시점에서 콜백함수를 호출하게 만들 수 있습니다.
Camera 이동 관련 callback
camera 이동 관련 API 호출 시 파라미터로 콜백을 전달할 수 있습니다. 이동이 끝나고 나면, 파라미터로 전달된 콜백이 호출됩니다. 아래 예제는 animateCamera API에 콜백을 전달하여, animateCamera 동작이 완료되고 나서 호출되는 콜백을 이용한 예제입니다.
class ApiCallbackSample: APISampleBaseViewController {
override func addViews() {
let defaultPosition: MapPoint = MapPoint(longitude: 126.978365, latitude: 37.566691)
let mapviewInfo: MapviewInfo = MapviewInfo(viewName: "mapview", viewInfoName: "map", defaultPosition: defaultPosition)
mapController?.addView(mapviewInfo)
}
func createPois() {
// Poi가 속할 레이어 생성
let mapView: KakaoMap? = mapController?.getView("mapview") as? KakaoMap
let labelManager = mapView?.getLabelManager()
let layer = labelManager?.addLabelLayer(option: LabelLayerOptions(layerID: "PoiLayer", competitionType: .none, competitionUnit: .poi, orderType: .rank, zOrder: 0))
// Poi가 그려질 스타일 생성
let iconStyle = PoiIconStyle(symbol: UIImage(named: "mapIcoBookmark_01.png")!, anchorPoint: CGPoint(x: 0.0, y: 0.5))
let perLevelStyle = PerLevelPoiStyle(iconStyle: iconStyle, level: 0)
let poiStyle = PoiStyle(styleID: "PoiStyle1", styles: [perLevelStyle])
labelManager?.addPoiStyle(poiStyle)
// Pois 생성
let coord = MapPoint(longitude: 126.875658, latitude: 37.492889)
let boundary = GeoCoordinate(longitude: 0.026949, latitude: 0.035932)
let options = PoiOptions(styleID: "PoiStyle1")
var positions = [MapPoint]()
for _ in 1 ... 100 {
let rand = GeoCoordinate(
longitude: Double.random(in: 0...boundary.longitude),
latitude: Double.random(in: 0...boundary.latitude))
positions.append(MapPoint(longitude: coord.wgsCoord.longitude + rand.longitude,
latitude: coord.wgsCoord.latitude + rand.latitude))
}
// Pois 추가 -> 카메라 애니메이션 -> show Pois
let _ = layer?.addPois(option: options, at: positions) { [weak mapView, weak layer](pois: [Poi]?) -> Void in //addPois 완료 콜백 추가
guard let view = mapView else { return }
if let items = pois {
let rect = AreaRect(points: items.map{ $0.position }) // 추가된 poi를 기반으로 areaRect를 생성하여 카메라를 이동시킨다.
view.animateCamera( cameraUpdate: CameraUpdate.make(area: rect), options: CameraAnimationOptions(autoElevation: false, consecutive: false, durationInMillis: 2000)) { () -> Void in
guard let labelLayer = layer else { return }
labelLayer.showAllPois() // 카메라 이동이 완료되면 layer에 추가된 모든 poi를 표시한다.
}
}
}
}
}
객체 추가/삭제시 callback
예를 들어, Poi의 경우 LabelLayer의 Poi를 추가하는 함수에서 아래와 같이 optional로 callback함수를 파라미터에 추가할 수 있습니다.
addPoi(option:at:callback:)-> Poi
addPois(option:at:callback:) -> [Poi]
addPois(options:at:callback:) -> [Poi]
추가한 Poi만큼 생성이 완료되면, 파라미터로 전달된 콜백 함수를 호출합니다. 콜백 함수는 생성한 Poi를 인자로 갖습니다. 아래 예제는 여러개의 Poi 추가가 완료되면, 그 Poi를 포함하는 카메라 영역으로 이동 한 후 Poi를 보여주는 예제입니다.
func createPois() {
// Poi가 속할 레이어 생성
let mapView: KakaoMap? = mapController?.getView("mapview") as? KakaoMap
let labelManager = mapView?.getLabelManager()
let layer = labelManager?.addLabelLayer(option: LabelLayerOptions(layerID: "PoiLayer", competitionType: .none, competitionUnit: .poi, orderType: .rank, zOrder: 0))
// Poi가 그려질 스타일 생성
let iconStyle = PoiIconStyle(symbol: UIImage(named: "mapIcoBookmark_01.png")!, anchorPoint: CGPoint(x: 0.0, y: 0.5))
let perLevelStyle = PerLevelPoiStyle(iconStyle: iconStyle, level: 0)
let poiStyle = PoiStyle(styleID: "PoiStyle1", styles: [perLevelStyle])
labelManager?.addPoiStyle(poiStyle)
// Pois 생성
let coord = MapPoint(longitude: 126.875658, latitude: 37.492889)
let boundary = GeoCoordinate(longitude: 0.026949, latitude: 0.035932)
let options = PoiOptions(styleID: "PoiStyle1")
var positions = [MapPoint]()
for _ in 1 ... 100 {
let rand = GeoCoordinate(
longitude: Double.random(in: 0...boundary.longitude),
latitude: Double.random(in: 0...boundary.latitude))
positions.append(MapPoint(longitude: coord.wgsCoord.longitude + rand.longitude,
latitude: coord.wgsCoord.latitude + rand.latitude))
}
// Pois 추가 -> 카메라 애니메이션 -> show Pois
let _ = layer?.addPois(option: options, at: positions) { [weak mapView, weak layer](pois: [Poi]?) -> Void in //addPois 완료 콜백 추가
guard let view = mapView else { return }
if let items = pois {
let rect = AreaRect(points: items.map{ $0.position }) // 추가된 poi를 기반으로 areaRect를 생성하여 카메라를 이동시킨다.
view.animateCamera(cameraUpdate: CameraUpdate.make(area: rect), options: CameraAnimationOptions(autoElevation: false, consecutive: false, durationInMillis: 2000)) { () -> Void in
guard let labelLayer = layer else { return }
labelLayer.showAllPois() // 카메라 이동이 완료되면 layer에 추가된 모든 poi를 표시한다.
}
}
}
}
위와 같은 종류의 callback은 Gui를 제외한 모든 type의 객체(Label, Shape, Route)에 추가할 수 있습니다.
Callback사용시 주의사항
API에서는 위와 같은 형태의 콜백을 optional로 받을 수 있게 되어있으나, 이러한 형태의 callback을 기반으로 코드를 구성하는것은 권장하지 않습니다. 기본적으로 지도 엔진은 사용자가 API를 호출한 순서대로 동작의 시퀀스가 보장됩니다. 예를 들어, addPoi를 한 이후 showPoi를 호출한 경우, 반드시 Poi가 추가된 이후에 Poi가 화면에 표시됩니다. 따라서, 특수한 경우가 아닌 이상 위와 같은 종류의 callback함수는 사용하지 않는것을 권장합니다.