Search code examples
swiftxcodeswiftuimapboxmglmapview

Centering a map annotation to the top quarter of the screen


so I'd like to go from image 1 to image 2 when an annotation is clicked (Mapbox):

https://i.sstatic.net/OFIFa.png

It's fairly easy to have the map center on the annotation, by calling mapView.setCenter() inside one of the Mapbox delegate functions as follows:

func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
    mapView.setCenter(annotation.coordinate, animated: true)
}

Obviously, this centers the annotation to the middle of the screen, but I need the annotation to be 'centered' to in the area above the view that pops up so it's still visible (ideally, it'll be equidistant from the top of 'View' and the top edge of the screen).

I was thinking of setting the zoomLevel in setCenter and then zooming to a specific distance south of the annotation, but the problem with this is various screen sizes on iOS devices will have the annotation centered differently.

I was also thinking maybe I could do some kind of coordinate conversion from the map to a CGPoint on the screen but am really unsure about how to implement this correctly (I've only ever used mapView.convert(CGPoint,toCoordinateFrom: mapView), and this won't be useful here). I'm not sure how to approach this problem. Any help would be much appreciated, whether it's just getting me started on a path or if you already have a solution that's even better. Thanks!


Solution

  • I don't use MapBox but if you can access the region's span (horizontal and vertical distance of the map)

    You can subtract the desired amount of the latitude's span from the annotation's coordinate and set that center for the map.

        ///Centers the map region by placing the passed annotation in the center of the I quadrant
        func centerTop(annotation: SpecialAnnot){
            //Find a 4th of the span (The horizontal and vertical span representing the amount of map to display.)
            let latCorrection = region.span.latitudeDelta/4 + region.span.latitudeDelta/8
            
            self.region = MKCoordinateRegion(center: .init(latitude: annotation.coordinate.latitude - latCorrection, longitude: annotation.coordinate.longitude), span: region.span)
        }
    

    Here is some SwiftUI code.

    import SwiftUI
    import MapKit
    class SpecialAnnot: NSObject, MKAnnotation, Identifiable{
        let id: UUID = .init()
        var coordinate: CLLocationCoordinate2D
        init(coordinate: CLLocationCoordinate2D) {
            self.coordinate = coordinate
            super.init()
        }
        
    }
    struct QuarterCenteredView: View {
        @State var region: MKCoordinateRegion = MKCoordinateRegion()
        
        let annotationCoordinate: [SpecialAnnot] = [SpecialAnnot(coordinate: .init(latitude: 40.748817, longitude: -73.985428))]
        var body: some View {
            VStack{
                Button("center quadrant I", action: {
                    centerTop(annotation: annotationCoordinate.first!)
                })
                Map(coordinateRegion: $region, annotationItems: annotationCoordinate, annotationContent: { annot in
                    MapAnnotation(coordinate: annot.coordinate, content: {
                        Image(systemName: "mappin").foregroundColor(.red)
                    })
                })
                    .onAppear(perform: {
                        DispatchQueue.main.async {
                            region = MKCoordinateRegion(center: annotationCoordinate.first!.coordinate, span: region.span)
                        }
                    })
            }
        }
        ///Centers the map region by placing the passed annotation in the center of the I quadrant
        func centerTop(annotation: SpecialAnnot){
            //Find a 4th of the span (The horizontal and vertical span representing the amount of map to display.)
            let latCorrection = region.span.latitudeDelta/4 + region.span.latitudeDelta/8
            
            self.region = MKCoordinateRegion(center: .init(latitude: annotation.coordinate.latitude - latCorrection, longitude: annotation.coordinate.longitude), span: region.span)
        }
    }
    
    struct QuarterCenteredView_Previews: PreviewProvider {
        static var previews: some View {
            QuarterCenteredView()
        }
    }