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'
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.
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
}
}