Home > Software design >  continuous center of user with Mapkit
continuous center of user with Mapkit

Time:05-07

Good afternoon,

I am having trouble displaying a map where it only centers around the user and will stay on the user with movement. My error is in my view file where I mark //HERE.

My error is Type '()' cannot conform to 'View'

  1. Why is it that this line is giving me an error? If that line runs, my assumption would be that the map region is changing but it still would return the Map which conforms. View file is presented below.

  2. If I run it without the commented line, it does not display my current location. I have my simulator settings are set to Features > Location > Apple. Even when I am zooming way out, nothing is marked on the map.

    
            import SwiftUI
            import MapKit
    
            struct Location: Identifiable {
            let id = UUID()
    
            let name: String
            let content: String?
            let lat: Double
            let lon: Double
            var dangerLevel: CGFloat? = 10.0
            var coord: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: lat, longitude: lon) }
        }
    
        struct ContentView: View {
            @EnvironmentObject var locationManager: LocationManager
            @State private var userTrackingMode: MapUserTrackingMode = .follow
    
            @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 40.7128, longitude: 74.0060), span: MKCoordinateSpan( latitudeDelta: 0.03, longitudeDelta: 0.03))
    
            var body: some View {
                var locationManager = LocationManager()
                region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan( latitudeDelta: 0.03, longitudeDelta: 0.03)) // HERE!!!!
    
    
    
                VStack {
                    Map(coordinateRegion: $region,
                        interactionModes: .all,
                        showsUserLocation: true)
    
                }
    
            }
        }
    
        struct ContentView_Previews: PreviewProvider {
            static var previews: some View {
                ContentView().environmentObject(LocationManager())
            }
        }
    
    
    
    

This is the next file where I define my locationManager

import Foundation
import CoreLocation

class LocationManager: NSObject, ObservableObject {
    private let locationManager = CLLocationManager()
    @Published var location: CLLocation?
    
    override init() {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }
}
extension LocationManager : CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager,
                         didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        self.location = location
    }
    
    
}

CodePudding user response:

There were a couple of issues with your code. First, to answer the question asked, you should refrain from putting variables that are NOT views directly into the var body. While there are ways of getting around this restriction, there is not good reason to any longer. Since region is not a view, the code through the error. And yes, I know you defined var locationManager and the ViewBuilder took it as it was the init of a variable, not the variable itself. However, you already ha a reference to locationManager that you defined in the header. Use that.

I put a few more changes into your code with comments to help things along. Let me know if you have further questions.

struct ContentView: View {
    // Unless you are using locationManager all over your code, you don't need to pass it as an
    // .environmentObject, though you can if needed. Since this is the first instance in this example
    // of locationManager, I made it a @StateObject.
    @StateObject var locationManager = LocationManager()
    @State private var userTrackingMode: MapUserTrackingMode = .follow
    @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.60697453, longitude: -122.42798519), span: MKCoordinateSpan( latitudeDelta: 0.03, longitudeDelta: 0.03))

    var body: some View {
        VStack {
            Map(coordinateRegion: $region,
                interactionModes: .all,
                showsUserLocation: true)
            // Since locationManager is an ObservableObject, you can watch for changes with .onChange(of:)
            .onChange(of: locationManager.location) { newLocation in
                // Never force unwrap an optional unless you just set it yourself in the code.
                guard let newLocation = newLocation else { return }
                region = MKCoordinateRegion(center: newLocation.coordinate, span: MKCoordinateSpan( latitudeDelta: 0.03, longitudeDelta: 0.03)) // HERE!!!!
            }
        }
        
    }
}

import CoreLocation

// I would consider renaming this class. It can be confusing to see
// locationManager.locationManager in code.
class LocationManager: NSObject, ObservableObject {
    private let locationManager = CLLocationManager()
    @Published var location: CLLocation?

    override init() {
        super.init()
        // You can generally drop .self, with some exceptions. The compiler will correct you.
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
    }
}

extension LocationManager : CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager,
                         didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        // This is an exception to dropping self when a variable in a closure has the same name as a
        // self variable.
        self.location = location
    }
}
  • Related