[FAQ] 지도/로컬 API 문의 전 꼭 읽어 주세요.
https://devtalk.kakao.com/t/faq-api/125610
리스트에 있는 모든 장소에 poi를 생성하고 싶은데, 다중마커에 대한 예시코드가 있을까요? 찾아봐도 웹용 밖에 못찾겠어서요…
[FAQ] 지도/로컬 API 문의 전 꼭 읽어 주세요.
https://devtalk.kakao.com/t/faq-api/125610
리스트에 있는 모든 장소에 poi를 생성하고 싶은데, 다중마커에 대한 예시코드가 있을까요? 찾아봐도 웹용 밖에 못찾겠어서요…
3개의 장소 리스트가 있고, 각 장소리스트의 레이어를 분리해서, 장소타입 선택시 해당 리스트들의 장소에 모두 poi가 찍히는 코드를 작성중입니다. 리스트 내 모든 장소에 Poi가 찍히는 게 하는 것까지는 해결했는데, 모든 Label Layer가 한번에 같이 나오고 클릭이벤트 시 계속 반복해서 생성되는데, 한꺼번에 Layer자체를 숨기거나 제거하는 건 어떻게 해야 할지 모르겠습니다.
또, 동일 Layer가 계속 재생성되는 문제는 어떻게 해결해야할까요??
아래는 제가 작성중인 코드입니다.
struct KakaoMapView: UIViewRepresentable {
@Binding var draw: Bool
@Binding var targetLocation: CLLocationCoordinate2D
var places: [Store]
var label: String
/// UIView를 상속한 KMViewContainer를 생성한다.
/// 뷰 생성과 함께 KMControllerDelegate를 구현한 Coordinator를 생성하고, 엔진을 생성 및 초기화한다.
func makeUIView(context: Self.Context) -> KMViewContainer {
let view: KMViewContainer = KMViewContainer()
view.sizeToFit()
context.coordinator.createController(view)
context.coordinator.mapController?.initEngine()
return view
}
/// Updates the presented `UIView` (and coordinator) to the latest
/// configuration.
/// draw가 true로 설정되면 엔진을 시작하고 렌더링을 시작한다.
/// draw가 false로 설정되면 렌더링을 멈추고 엔진을 stop한다.
func updateUIView(_ uiView: KMViewContainer, context: Self.Context) {
if draw {
context.coordinator.mapController?.startEngine()
context.coordinator.mapController?.startRendering()
// Label Layer와 Poi Style 생성
context.coordinator.createLabelLayer()
context.coordinator.createPoiStyle()
//Optional("PoiLayer") already exists.
// 기존의 POI 레이어를 제거하고 새로운 레이어를 생성해야함.
}
else {
context.coordinator.mapController?.stopRendering()
context.coordinator.mapController?.stopEngine()
}
if targetLocation.latitude != 0.0 && targetLocation.longitude != 0.0 {
context.coordinator.moveCamera(longitude: targetLocation.longitude, latitude: targetLocation.latitude, zoomLevel: 6)
for place in places {
switch place.store_type {
case "Recent":
context.coordinator.createPois(longitude: place.location.longitude, latitude: place.location.latitude, label: "", layerID: "RecentLayer")
case "type2":
context.coordinator.createPois(longitude: place.location.longitude, latitude: place.location.latitude, label: "", layerID: "type2Layer")
case "type3":
context.coordinator.createPois(longitude: place.location.longitude, latitude: place.location.latitude, label: "", layerID: "type3Layer")
default:
break
}
}
}
}
/// Coordinator 생성
func makeCoordinator() -> KakaoMapCoordinator {
return KakaoMapCoordinator()
}
/// Cleans up the presented `UIView` (and coordinator) in
/// anticipation of their removal.
static func dismantleUIView(_ uiView: KMViewContainer, coordinator: KakaoMapCoordinator) {
}
/// Coordinator 구현. KMControllerDelegate를 adopt한다.
class KakaoMapCoordinator: NSObject, MapControllerDelegate, CLLocationManagerDelegate {
var mapController: KMController?
var first: Bool
let locationManager = CLLocationManager()
// SimplePOI class methods
var poiStyle: PoiStyle?
override init() {
first = true
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
// KMController 객체 생성 및 event delegate 지정
func createController(_ view: KMViewContainer) {
mapController = KMController(viewContainer: view)
mapController?.delegate = self
}
// KMControllerDelegate Protocol method구현
/// 엔진 생성 및 초기화 이후, 렌더링 준비가 완료되면 아래 addViews를 호출한다.
/// 원하는 뷰를 생성한다.
@objc func addViews() {
if let userLocation = locationManager.location?.coordinate {
//print("userLocation Longitude: \(userLocation.longitude), Latitude: \(userLocation.latitude)")
let defaultPosition: MapPoint = MapPoint(longitude: userLocation.longitude, latitude: userLocation.latitude)
let mapviewInfo: MapviewInfo = MapviewInfo(viewName: "mapview", viewInfoName: "map", defaultPosition: defaultPosition)
mapController?.addView(mapviewInfo)
}
}
//addView 성공 이벤트 delegate. 추가적으로 수행할 작업을 진행한다.
func addViewSucceeded(_ viewName: String, viewInfoName: String) {
//print("OK") //추가 성공. 성공시 추가적으로 수행할 작업을 진행한다.
}
//addView 실패 이벤트 delegate. 실패에 대한 오류 처리를 진행한다.
func addViewFailed(_ viewName: String, viewInfoName: String) {
//print("Failed")
}
/// KMViewContainer 리사이징 될 때 호출.
func containerDidResized(_ size: CGSize) {
let mapView: KakaoMap? = mapController?.getView("mapview") as? KakaoMap
mapView?.viewRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)
if first, let userLocation = locationManager.location?.coordinate {
let cameraUpdate: CameraUpdate = CameraUpdate.make(target: MapPoint(longitude: userLocation.longitude, latitude: userLocation.latitude), zoomLevel: 6, mapView: mapView!)
mapView?.moveCamera(cameraUpdate)
first = false
}
}
func moveCamera(longitude: Double, latitude: Double, zoomLevel: Float) {
guard let mapView = mapController?.getView("mapview") as? KakaoMap else {
//print("Failed to get KakaoMap view.")
return
}
// CameraUpdateType을 CameraPosition으로 생성하여 지도의 카메라를 특정 좌표로 이동시킨다. MapPoint, 카메라가 바라보는 높이, 회전각 및 틸트를 지정할 수 있다.
mapView.moveCamera(CameraUpdate.make(target: MapPoint(longitude: longitude, latitude: latitude), zoomLevel: 18, mapView: mapView))
}
// POI가 속할 LabelLayer를 생성한다.
func createLabelLayer() {
guard let mapView = mapController?.getView("mapview") as? KakaoMap else {
//print("Failed to get KakaoMap view.")
return
}
let labelManager = mapView.getLabelManager() //LabelManager를 가져온다. LabelLayer는 LabelManger를 통해 추가할 수 있다.
// let layerOption = LabelLayerOptions(layerID: "PoiLayer", competitionType: .none, competitionUnit: .poi, orderType: .rank, zOrder: 10001)
// let _ = labelManager.addLabelLayer(option: layerOption)
// RecentLayer 생성
let recentLayerOption = LabelLayerOptions(layerID: "RecentLayer", competitionType: .none, competitionUnit: .poi, orderType: .rank, zOrder: 10001)
let _ = labelManager.addLabelLayer(option: recentLayerOption)
// type2Layer 생성
let type2LayerOption = LabelLayerOptions(layerID: "type2Layer", competitionType: .none, competitionUnit: .poi, orderType: .rank, zOrder: 10002)
let _ = labelManager.addLabelLayer(option: type2LayerOption)
// type3Layer 생성
let type3LayerOption = LabelLayerOptions(layerID: "type3Layer", competitionType: .none, competitionUnit: .poi, orderType: .rank, zOrder: 10003)
let _ = labelManager.addLabelLayer(option: type3LayerOption)
//print("createLabelLayer")
}
func createPoiStyle() {
guard let mapView = mapController?.getView("mapview") as? KakaoMap else {
//print("Failed to get KakaoMap view.")
return
}
let labelManager = mapView.getLabelManager()
// 심볼을 지정.
// 심볼의 anchor point(심볼이 배치될때의 위치 기준점)를 지정. 심볼의 좌상단을 기준으로 한 % 값.
let iconStyle = PoiIconStyle(symbol: UIImage(named: "ic_position_pin_shadow"), anchorPoint: CGPoint(x: 0.5, y: 0.8))
// text Style 지정
var textLineStyles = [PoiTextLineStyle]()
let text = TextStyle(fontSize: 25, fontColor: UIColor.black, strokeThickness: 3, strokeColor: UIColor.white)
let textLineStyle = PoiTextLineStyle(textStyle: text)
textLineStyles.append(textLineStyle)
let textStyle = PoiTextStyle(textLineStyles: textLineStyles)
textStyle.textLayouts = [.bottom]
let perLevelStyle = PerLevelPoiStyle(iconStyle: iconStyle, textStyle: textStyle, level: 0) // 이 스타일이 적용되기 시작할 레벨.
let poiStyle = PoiStyle(styleID: "customStyle1", styles: [perLevelStyle])
labelManager.addPoiStyle(poiStyle)
//print("createPoiStyle")
//print(iconStyle)
}
// POI를 생성한다.
func createPois(longitude: Double, latitude: Double, label: String, layerID: String) {
guard let mapView = mapController?.getView("mapview") as? KakaoMap else {
return
}
let labelManager = mapView.getLabelManager()
let layer = labelManager.getLabelLayer(layerID: layerID) // 생성한 POI를 추가할 레이어를 가져온다.
let poiOption = PoiOptions(styleID: "customStyle1") // 생성할 POI의 Option을 지정하기 위한 자료를 담는 클래스를 생성. 사용할 스타일의 ID를 지정한다.
poiOption.rank = 0
poiOption.addText(PoiText(text: label, styleIndex: 0)) // PoiTextLineStyle[0] 적용
let poi1 = layer?.addPoi(option: poiOption, at: MapPoint(longitude:longitude, latitude: latitude), callback: {(_ poi: (Poi?)) -> Void in
})
poi1?.show()
}
}
}
@_1038
우선 POI 생성 관련 코드를 다른 곳으로 옮기시는 것이 맞습니다. 지금 올려주신 코드상의 위치는 적절하지 않습니다.
updateUIView 함수는 view 상태 업데이트가 됨에 따라 반복적으로 호출될 수 있습니다. 이에 대한 자세한 내용은 애플 공식문서( updateUIView(_:context:) | Apple Developer Documentation) 나 관련 자료를 참고 하시기 바랍니다.
LabelLayer에는 layer의 visible 속성을 지정할 수 있으며, layer에 속한 label 들을 개별 혹은 모두 숨기거나 삭제할 수 있습니다. 이에 대한 부분은 API 문서를 참고하시기 바랍니다.
visible 속성은 어떻게 지정하나요?