pauljohanneskraft / Map

MKMapView wrapper for SwiftUI as drop-in to MapKit's SwiftUI view. Easily extensible annotations and overlays, iOS 13 support and backwards compatible with MKAnnotation and MKOverlay!
MIT License
191 stars 49 forks source link

[WIP] Expose map camera and allow direct transformations of modified annotations and annotation views #19

Closed alex-reilly-pronto closed 2 years ago

alex-reilly-pronto commented 2 years ago

Hi! Thanks for maintaining this library. It's been a pleasure to use. I've got some annotations where I want to maintain their orientation relative to the map and move them over time, so I'm exposing the camera along with a closure that gives the user direct access to modified annotations and annotation views.

The suggested method online for changing annotation location is to use a MKPointAnnotation rather than an MKAnnotation, and to alter the coordinate field and for changing annotation orientation is to do a CGAffineTransform on the view itself. I'm doing both, but I'm open to something else if you've got a suggestion. It does feel like this requires developers to touch UIKit more than you'd probably like.

Here's a snippet of it in action.

// ...
.modifiedAnnotations { modifiedAnnotation, mkannotation, view in
  mkannotation.coordinate = modification.coordinate
  view.transform = CGAffineTransform(
    rotationAngle: modifiedAnnotation.annotationHeading.degreesToRadians
  )
}
.camera(viewStore.binding(get: \.camera, send: MapAction.moveCamera))

Here we've got the camera bound to a field in my view's state, which is being used to calculate annotationHeading behind the scenes.

Prior to this, if an annotation was modified it wouldn't be updated because Map.Coordinator was only checking for inserted or removed ids. Because of that, annotations are now required to be Equatable so that the coordinator can check for modified annotations in addition to inserted and removed annotations.

If we naively remove and then insert modified annotations, then that causes the annotation to flicker.

Breaking Changes

pauljohanneskraft commented 2 years ago

Hey @alex-reilly-pronto ! Thank you for your contribution!

Unfortunately, there are still some things to improve on, so here is a first feedback from my side:

Thank you once again for your contribution. Please let me know, what you think about this and whether you want to continue working on this pull request 😊

alex-reilly-pronto commented 2 years ago

It might make sense to back up and reassess what it is I want here. I think the ideal setup would let me do something like

func truckAnnotation(annotation: TruckAnnotationModel) -> MapAnnotation {
  return ViewMapAnnotation(coordinate: annotation.location.coordinate) {
    ZStack {
      Circle()
        .foregroundColor(.indigo)

      Image(systemName: "location.north.fill")
        .foregroundColor(.white)
        .frame(width: 20, height: 20)
    }
    .frame(width: 35, height: 35)
    .background(Color.red)
    .rotation(Angle(degrees: annotation.annotationHeading) // <- important part
  }
}

But this doesn't work because the view is created exactly once when it first shows up on screen. Maybe that restriction is unnecessary and can be worked around.

alex-reilly-pronto commented 2 years ago

Closing this out for now.