October 28, 2023

38. Карта с кластеризацией в SwiftUI

В ходе работы над приложением с площадками для воркаута я разобрался как реализовать в SwiftUI карту с кластеризацией (объединение рядом стоящих точек при отдалении) и сейчас покажу как это делается.

На момент публикации статьи (октябрь 2023 года) карта в SwiftUI до сих пор не умеет в кластеризацию, поэтому нам пригодится MKMapVIew и UIViewRepresentable 😁

Скриншоты экрана с применением такой карты:

Карта с кластеризацией (кластеры желтые)
Приблизили карту, кластеров стало больше

На что нужно обратить внимание

1) makeUIView - тут карта создается "в первый раз", но чтобы избежать дублей, можно один раз сохранить готовый экземпляр и обращаться к нему в следующий раз:

let view = if let storedView = MapView991.storedMapView {
  storedView
} else {
  MKMapView()
}
...
return view

2) updateUIView - тут происходит обновление карты, например при изменении набора точек для отображения (при фильтрации, как вариант), или при изменении региона карты. Важно правильно проверить точки и регион, чтобы карта не мерцала из-за лишних обновлений. В случае с моей картой я фильтрую кластеры и точку самого пользователя перед сопоставлением точек:

RegionOrganizer(old: mapView.region, new: region)
  .updateRegionIfNeeded(for: mapView)
AnnotationsOrganizer(old: mapView.annotations, new: annotations)
  .updateAnnotationsIfNeeded(for: mapView)

3) mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) - а тут как раз создаются точки для отображения, в том числе кластеры:

let view: MKMarkerAnnotationView
switch annotation {
case is MKUserLocation: return nil // игнорируем дефолтную точку с пользователем
case is MKClusterAnnotation: // делаем кластер
  view = mapView.dequeueReusableAnnotationView(withIdentifier: clusterID) as? MKMarkerAnnotationView
  ?? .init(annotation: annotation, reuseIdentifier: clusterID)
  ...
default: // делаем обычную точку
  view = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? MKMarkerAnnotationView
  ?? .init(annotation: annotation, reuseIdentifier: annotationID)
  ...
}
return view

Библиотеку можно посмотреть тут, а почитать другие статьи - тут.