Reputation: 15247
Setup:
I am converting an app to SwiftUI. This app had an MKMapView
with annotations that could be dragged on the map.
I was able to write a first version of the SwiftUI app with a draggable image.
It uses a View
that is displayed as annotation on the map:
struct AnnotationView: View {
var buyLocationNameBeforeDragging: String
@State private var isDragging = false
@GestureState var dragAmount = CGSize.zero
var region: MKCoordinateRegion
func coordinateFrom(region: MKCoordinateRegion, dragAmount: CGSize) -> CLLocationCoordinate2D {
let coordinate = CLLocationCoordinate2D() // How to compute this?
return coordinate
}
var drag: some Gesture {
DragGesture()
.onChanged { _ in
self.isDragging = true
}
.onEnded { _ in
self.isDragging = false
let coreDataManager = testOrPreview ? CoreDataManager.test : CoreDataManager.shared
let newCoordinate = coordinateFrom(region: region, dragAmount: dragAmount)
// Move the dragged place to the new coordinate
// …
}
.updating($dragAmount) { value, state, transaction in
state = value.translation
}
}
var body: some View {
Image("PinRed")
.offset(dragAmount)
.defersSystemGestures(on: .all)
.onTapGesture {
print("tapped")
}
.gesture(drag)
}
}
It is possible to tap the annotation pin, and - after tapping longer - to drag the pin to a new location.
Problem:
To update my model, I need the endpoint coordinates of the drag on the map. These should be computed in func coordinateFrom
(see above), but I have no idea how to do this.
MKMapView
has e.g. a function convert(_:toCoordinateFrom:)
, but SwiftUI's Map
has nothing like this.
Question:
How to do the conversion?
Upvotes: 2
Views: 1055
Reputation: 15247
Problem solved.
I 1st converted var drag: some Gesture
to a function:
func drag(geo: GeometryProxy) -> some Gesture {
DragGesture(coordinateSpace: .named("screen"))
.onChanged { value in
self.isDragging = true
}
.onEnded { _ in
self.isDragging = false
let coreDataManager = testOrPreview ? CoreDataManager.test : CoreDataManager.shared
let newCoordinate = coordinateFrom(region: region, geo: geo, dragAmount: dragAmount)
// Move the dragged place to the new coordinate
// …
}
.updating($dragAmount) { value, state, transaction in
state = value.translation
}
}
where geo
is supplied by the body of the AnnotationView
, and .coordinateSpace(name: "screen")
has been applied to the view displaying the map:
var body: some View {
GeometryReader { geo in
Image("PinRed")
.offset(dragAmount)
.defersSystemGestures(on: .all)
.onTapGesture {
print("Global center: \(geo.frame(in: .global).midX) x \(geo.frame(in: .global).midY)")
}
.gesture(drag(geo: geo))
}
.frame(width: 40, height: 30)
}
Next, I converted the map coordinates from screen coordinates to map coordinates using the old MKMapView
as:
func coordinateFrom(region: MKCoordinateRegion, geo: GeometryProxy, dragAmount: CGSize) -> CLLocationCoordinate2D {
let mkMapView = MKMapView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
mkMapView.region = region
let frame = geo.frame(in: .global)
let dragEndPoint = CGPoint(x: frame.origin.x + dragAmount.width, y: frame.origin.y + dragAmount.height)
let dragEndCFoordinate = mkMapView.convert(dragEndPoint, toCoordinateFrom: mkMapView)
return dragEndCFoordinate
}
Here, I assume the MKMapView
and SwiftUI Map
use the same coordinate transforms. In my case, I don't see any differences.
Upvotes: 3