👉 사용자의 실시간 위치를 지도에 표시할 때, 심볼로 원형 위에 프로필이미지를 겹친 뷰를 이미지 대신 띄우는 것이 목표.
👉 카카오맵에서 지원하는 마커(Poi) 형식은 UIImage로 한정되어 있어, 커스텀 마커를 띄울 수 없었음
👉 심볼이 될 뷰를 만들고, Extention을 통해 뷰를 UIImage로 변환시키는 함수 작성
ProfileImageView:
import SwiftUI
// 프로필 이미지를 위한 SwiftUI View
struct ProfileImageView: View {
let image: Image
var body: some View {
ZStack {
Image("icon-location-anchor")
.frame(width: LayoutAdapter.shared.scale(value: 11), height: LayoutAdapter.shared.scale(value: 11))
.padding(.top, 55)
Image("icon-location-pin")
.frame(width: LayoutAdapter.shared.scale(value: 45), height: LayoutAdapter.shared.scale(value: 56))
image
.resizable()
.scaledToFill()
.frame(width: LayoutAdapter.shared.scale(value: 37), height: LayoutAdapter.shared.scale(value: 37))
.clipShape(Circle())
.frame(width: LayoutAdapter.shared.scale(value: 10), height: LayoutAdapter.shared.scale(value: 10))
.padding(.bottom, 10)
}
}
}
Extensions:
// MARK: View to UIImage - 실시간 위치 조회시 프로필사진 마킹용
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self.edgesIgnoringSafeArea(.all))
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
// MARK: 이미지 크기 조절
extension UIImage {
func resizedForProfile(to size: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { _ in
draw(in: CGRect(origin: .zero, size: size))
}
}
}
MapPinView:
// Poi 표시 스타일 생성
func createPoiStyle() {
guard let view = controller?.getView(mapViewName) as? KakaoMap else {
print("view is nil in createPoiStyle")
return
}
let manager = view.getLabelManager()
// 내 마커용 스타일 생성: SwiftUI View를 UIImage로 변환
let myMarker = ProfileImageView(image: Image(myLocation?.member?.profileImage ?? "icon-profile-default"))
let mySymbolImage = myMarker.snapshot().resizedForProfile(to: CGSize(width: LayoutAdapter.shared.scale(value: 30), height: LayoutAdapter.shared.scale(value: 40.667)))
let myIconStyle = PoiIconStyle(symbol: mySymbolImage, anchorPoint: CGPoint(x: 0.5, y: 1))
let myPoiStyle = PoiStyle(styleID: "myPoiStyle", styles: [
PerLevelPoiStyle(iconStyle: myIconStyle, level: 12)
])
manager.addPoiStyle(myPoiStyle)
// 친구들 각각의 마커 스타일 생성
for (index, friend) in friendsLocation.enumerated() {
let profileImageName = friend.member?.profileImage ?? "icon-profile-default"
let friendMarker = ProfileImageView(image: Image(profileImageName))
let friendSymbolImage = friendMarker.snapshot().resizedForProfile(to: CGSize(width: LayoutAdapter.shared.scale(value: 30), height: LayoutAdapter.shared.scale(value: 40.667)))
let friendIconStyle = PoiIconStyle(symbol: friendSymbolImage, anchorPoint: CGPoint(x: 0.5, y: 1))
// 각 친구별로 고유한 styleID 생성
let styleID = "friendPoiStyle_\(index)"
let friendPoiStyle = PoiStyle(styleID: styleID, styles: [
PerLevelPoiStyle(iconStyle: friendIconStyle, level: 12)
])
manager.addPoiStyle(friendPoiStyle)
}
}
func createPois() {
guard let view = controller?.getView(mapViewName) as? KakaoMap else {
print("view is nil in createPois")
return
}
let manager = view.getLabelManager()
let layer = manager.getLabelLayer(layerID: "PoiLayer")
// 내 위치 POI 생성
let myPoiOption = PoiOptions(styleID: "myPoiStyle")
myPoiOption.rank = 0
if let myLocation {
let myPoi = layer?.addPoi(
option: myPoiOption,
at: MapPoint(longitude: myLocation.x, latitude: myLocation.y)
)
myPoi?.show()
}
// 친구들 위치 POI 생성 - 각각 다른 스타일 적용
for (index, friend) in friendsLocation.enumerated() {
let friendPoiOption = PoiOptions(styleID: "friendPoiStyle_\(index)")
friendPoiOption.rank = 1
let friendPoi = layer?.addPoi(
option: friendPoiOption,
at: MapPoint(longitude: friend.x, latitude: friend.y)
)
friendPoi?.show()
}
}