Rok Oblak
06/29/2024, 9:50 AMMKMapView
embedding in a Composable in the future, because the previous examples did not have click handling (which survives garbage collection), here is my take on it:
code in thread
Not perfect, if anyone sees anything to improve that would be great. It fits my use case of listening to a marker change from the hoisted state, but does not yet support center location / zoom change from the hoisted state.Rok Oblak
06/29/2024, 9:51 AM@OptIn(ExperimentalForeignApi::class)
@Composable
fun AppleMapView( modifier: Modifier, centerPos: MapPos, markerPos: MapPos?, initialZoom: Float, standard: Boolean,
onClick: (ClickedLatLng) -> Unit,
) {
val markerLocation = remember(markerPos) {
if (markerPos != null) {
CLLocationCoordinate2DMake(markerPos.lat, markerPos.long)
} else {
null
}
}
val currZoom = remember(initialZoom) { mutableStateOf(10_000.0 / initialZoom) }
val mkMapView = remember {
MKMapView().apply { setMapType(if (standard) MKMapTypeStandard else MKMapTypeSatellite) }
}
val mapGestureController = remember {
MapGestureController(mkMapView) { lat, long -> onClick(ClickedLatLng(lat, long)) }
}
val mapRegion = remember(centerPos, currZoom) {
val latLongMeters = currZoom.value
MKCoordinateRegionMakeWithDistance(centerCoordinate = CLLocationCoordinate2DMake(centerPos.lat, centerPos.long), latLongMeters, latLongMeters)
}
UIKitView(
modifier = modifier,
factory = {
mkMapView.apply {
mapGestureController.setupMapTapHandler()
setRegion(mapRegion, animated = false)
}
},
update = { mapView ->
mapView.annotations.forEach { mapView.removeAnnotation(it as MKAnnotationProtocol) }
if (markerLocation != null) {
mapView.addAnnotations(listOf(MKPointAnnotation(markerLocation)))
}
}
)
}
@OptIn(ExperimentalObjCName::class)
@ObjCName(name = "MapGestureController", swiftName = "MapGestureController", exact = true)
class MapGestureController(private val map: MKMapView, val onTapper: (latitude: Double, longitude: Double) -> Unit): NSObject() {
@OptIn(ExperimentalForeignApi::class)
fun setupMapTapHandler() {
val mapView = map
val tapRecognizer = UITapGestureRecognizer(target = this, action = sel_registerName("handleTap:"))
mapView.addGestureRecognizer(tapRecognizer)
}
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
@ObjCAction
fun handleTap(recognizer: UITapGestureRecognizer) {
val mapView = recognizer.view as? MKMapView ?: return
val touchPoint = recognizer.locationInView(mapView)
val coordsValue = mapView.convertPoint(touchPoint, toCoordinateFromView = mapView)
coordsValue.useContents {
onTapper(latitude, longitude)
}
}
}