UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: How can I show a users location on a map on first draw, then ignore location updates after that?

Forums > SwiftUI

I've hacked this to only run once but there must be a better way, how can I avoid:

func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
      if centerLocationOnce {
          let latDelta:CLLocationDegrees = 0.5
          let lonDelta:CLLocationDegrees = 0.5
          let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
          let region = MKCoordinateRegion(center: userLocation.coordinate, span: span)
          mapView.setRegion(region, animated: true)
          centerLocationOnce = false
      }
  }
//
//  MapView.swift
//  BAB Club Search
//
//  Created by Phil Stollery on 16/07/2020.
//

import MapKit
import SwiftUI

struct MapView: UIViewRepresentable {
    @Binding var centerCoordinate: CLLocationCoordinate2D
    @Binding var selectedPlace: MKPointAnnotation?
    @Binding var showingPlaceDetails: Bool
    @Binding var annotations: [MKPointAnnotation]

    var locationManager = CLLocationManager()
    func setupManager() {
      locationManager.desiredAccuracy = kCLLocationAccuracyReduced
      locationManager.requestWhenInUseAuthorization()
    }

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        setupManager()
        mapView.isRotateEnabled = false
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = true
        locationManager.stopUpdatingLocation()
        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        if view.annotations.count < 2 {
            view.removeAnnotations(view.annotations)
            view.addAnnotations(annotations)
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapView
        var centerLocationOnce = true

        init(_ parent: MapView) {
            self.parent = parent
        }

        func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
            parent.centerCoordinate = mapView.centerCoordinate
        }

        func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
            if centerLocationOnce {
                let latDelta:CLLocationDegrees = 0.5
                let lonDelta:CLLocationDegrees = 0.5
                let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
                let region = MKCoordinateRegion(center: userLocation.coordinate, span: span)
                mapView.setRegion(region, animated: true)
                centerLocationOnce = false
            }
        }

        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            // this is our unique identifier for view reuse
            let identifier = "ClubDetails"

            // attempt to find a cell we can recycle
            var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView

            if annotationView == nil {
                // we didn't find one; make a new one
                annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            } else {
                // we have a view to reuse, so give it the new annotation
                annotationView?.annotation = annotation
            }

            // Colour all the dojos green, but leave the uses location as a red marker
            // The title will ebe Approximate Location or My Location, so jyst check the end of the string
            let title = (annotationView?.annotation?.title)!! as String
            if title.suffix(8) != "Location" {
                annotationView?.markerTintColor = UIColor.systemGreen
            }

            // allow this to show pop up information
            annotationView?.canShowCallout = true

            // attach an information button to the view
            annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

            // whether it's a new view or a recycled one, send it back
            return annotationView
        }

        func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
            guard let placemark = view.annotation as? MKPointAnnotation else { return }

            parent.selectedPlace = placemark
            parent.showingPlaceDetails = true
        }
    }
}

2      

I would suggest not to use this mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) method at all, instead have that setRegion logic defied in your updateUIView(_ view: MKMapView, context: Context) for every small change in visible region of Map view we get this delegate method fired mapViewDidChangeVisibleRegion(_ mapView: MKMapView So, I don't see the point in using didUpdate userLocation method, below snippet may give you some idea, please review:

struct MapView: UIViewRepresentable {

    @Binding var centerCoordinate: CLLocationCoordinate2D
    var currentLocation: CLLocationCoordinate2D?
    var withAnnotation: MKPointAnnotation?

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapView

        init(_ parent: MapView) {
            self.parent = parent
        }

        func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
            if !mapView.showsUserLocation {
                parent.centerCoordinate = mapView.centerCoordinate
            }
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = false
        return mapView
    }

    func updateUIView(_ uiView: MKMapView, context: Context) {
        if let currentLocation = self.currentLocation {
            if let annotation = self.withAnnotation {
                uiView.removeAnnotation(annotation)
            }
            uiView.showsUserLocation = true
            let region = MKCoordinateRegion(center: currentLocation, latitudinalMeters: 800, longitudinalMeters: 800)
            uiView.setRegion(region, animated: true)
        } else if let annotation = self.withAnnotation {
            uiView.removeAnnotations(uiView.annotations)
            uiView.addAnnotation(annotation)
        }
    }
}

And set your current location like this

struct UserLocationView: View {

        var parent: LocationView

        init(_ parent: LocationView) {
            self.parent = parent
        }

        var body: some View {
            VStack {
                HStack {
                    Spacer()
                    Button(action: {
                        if let location = locationFetcher.lastKnownLocation {
                            print("Your location is \(location)")
                            parent.currentLocation = location
                            withAnimation {
                                parent.hideAddButton = true
                            }
                        } else {
                            print("Your location is unknown")
                        }
                    }) {
                        Image(systemName: "location.circle.fill")
                            .padding()
                            .background(Color.blue.opacity(0.75))
                            .foregroundColor(.white)
                            .font(.title)
                            .clipShape(Circle())
                            .rotationEffect(.degrees(45))
                            .padding(.trailing)
                    }
                }
            }
        }
    }

Hope this will help, code to entire project can be found here

2      

Thanks for the pointer, I didn't completely take your approach as I'm trying to do something different. For those who are curious the fix I did is here:

https://github.com/PhilStollery/BAB-Club-Search

and the app is in the app store here:

https://apps.apple.com/app/id1526804928

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.