Here's my code. As soon as I fire up the app in Simulator. I see following messages in the debug window.
2020-05-02 23:01:37.931720-0400 SwiftMapView[42245:6851218] Metal API Validation Enabled
2020-05-02 23:01:38.216200-0400 SwiftMapView[42245:6851218] libMobileGestalt MobileGestalt.c:1647: Could not retrieve region info
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var locationManager = CLLocationManager()
func setupManager() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
//locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
// Set Geofencing region
//let locValue:CLLocationCoordinate2D = self.locationManager.location!.coordinate
let locValue:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 15.833826, longitude: 78.845220)
let geofencingRegion: CLCircularRegion = CLCircularRegion(center:locValue, radius: 100, identifier: "Crap")
geofencingRegion.notifyOnExit = true
geofencingRegion.notifyOnEntry = true
// Start monitoring
locationManager.startMonitoring(for: geofencingRegion)
}
func makeUIView(context: Context) -> MKMapView {
setupManager()
let mapView = MKMapView(frame: UIScreen.main.bounds)
//mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 15.833826, longitude: 78.845220)
mapView.showsUserLocation = true
mapView.userTrackingMode = .follow
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
// Show current user location
//uiView.showsUserLocation = true
// Requst location when in use
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled()
&& (CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse)
{
//self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.startUpdatingLocation()
let locValue:CLLocationCoordinate2D = self.locationManager.location!.coordinate
let coordinate = CLLocationCoordinate2D(latitude: locValue.latitude, longitude: locValue.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0, longitudeDelta: 0)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
}
class MapAppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.locationManager = CLLocationManager()
self.locationManager!.delegate = self
return true
}
}
extension MapAppDelegate{
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Hello World")
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Welcome Home")
}
}
In simulator you have to add a location, then your code works for me (at least if your info.plist is correct):
Related
I was trying to display my current location into a Swiftui MapView. To do so, I created the following class:
import SwiftUI
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
#Published var locationManager = CLLocationManager()
#Published var locationStatus: CLAuthorizationStatus?
#Published var lastLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
private var statusString: String {
guard let status = locationStatus else {
return "unknown"
}
switch status {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationStatus = status
print(#function, statusString)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
lastLocation = location
// fetchLocation(location: lastLocation)
self.locationManager.stopUpdatingLocation()
print(#function, location)
}
}
and the following view:
var body: some View {
VStack {
Text("Latitude: \(locationManager.lastLocation?.coordinate.latitude ?? 0), Longitude: \(locationManager.lastLocation?.coordinate.longitude ?? 0)")
.onAppear{
print("DEBUG: status 1 : \(locationManager.lastLocation?.coordinate.latitude ?? 0)")
}
MapView(lat: (locationManager.lastLocation?.coordinate.latitude ?? 0), lon: locationManager.lastLocation?.coordinate.longitude ?? 0, latDelta: 0.05, lonDelta: 0.05)
.frame(width: UIScreen.screenWidth - 36, height: UIScreen.screenWidth / 2)
.cornerRadius(10)
.onAppear{
print("DEBUG: status 2 : \(locationManager.locationStatus)")
print("DEBUG: lat: \(locationManager.lastLocation?.coordinate.latitude), lon: \(locationManager.lastLocation?.coordinate.longitude)")
}
}
}
So far, the Textfield does show the correct coordinates, but my Mapview shows NIL as the coordinates.
Adding my MapView here as well for completeness:
struct MapView: UIViewRepresentable {
#State var lat = 0.0
#State var lon = 0.0
#State var latDelta = 0.05
#State var lonDelta = 0.05
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon )
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
Hope you could have a look and see if I have missed anything.
The answer is:
Remove all the #State from the MapView():
struct MapView: UIViewRepresentable {
var lat = 0.0
var lon = 0.0
var latDelta = 0.05
var lonDelta = 0.05
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon )
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
I have a location button that gives the region of $viewModel.region and it is defined inside of the locationButton's code. Then if the user types something inside of the designated TextField, the function will make that into its own region. How do I make it so that the map will change if the user taps on the location button, but then decides to enter an address.
Go to typed address;
func goToTypedAddress() {
geocoder.geocodeAddressString(goToAddress, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error ?? "")
}
if let placemark = placemarks?.first {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
print("Lat: \(coordinates.latitude) -- Long: \(coordinates.longitude)")
//added code
result = "Lat: \(coordinates.latitude) -- Long: \(coordinates.longitude)"
lat = coordinates.latitude
long = coordinates.longitude
}
})
print("\(lat)")
print("\(long)")
var goToAddressRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: lat, longitude: long), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
}
Location Button;
final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
#Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 40, longitude: 120), span: MKCoordinateSpan(latitudeDelta: 100, longitudeDelta: 100))
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
func requestAllowOnceLocationPermission() {
locationManager.requestLocation()
}
func locationManager( _ _manager:CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let latestLocation = locations.first else {
// show an error
return
}
DispatchQueue.main.async{
self.region = MKCoordinateRegion(
center: latestLocation.coordinate,
span:MKCoordinateSpan(latitudeDelta:0.05, longitudeDelta:0.05))
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
The Map;
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.ignoresSafeArea()
LocationButton(.currentLocation) {
viewModel.requestAllowOnceLocationPermission()
}
can someone please help me with my problem?
I have a SwiftUI Project and wrote a MKMapView File. I would like to store the Center coordinates in a #EnvironmentObject variable.
Unfortunately, I can't manage to pack the center coordinates into the EnvironmentObject variable.
my code looks like this:
struct MapView: UIViewRepresentable {
#EnvironmentObject var messpunkt_manager: Messpunkt_Manager
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.showsUserLocation = true // zeigt den aktuellen Standort des Users
map.delegate = context.coordinator // ermöglich die Bedienung
// shows annotations already in array
for location in messpunkt_manager.messpunkte {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
map.addAnnotation(annotation)
}
return map
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
// start coordinates
let coordinate = CLLocationCoordinate2D(latitude: messpunkt_manager.centerCoordinate.latitude, longitude: messpunkt_manager.centerCoordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: messpunkt_manager.centerZoom.latitudeDelta, longitudeDelta: messpunkt_manager.centerZoom.longitudeDelta)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
// Adds annotation to the map
for location in messpunkt_manager.messpunkte {
let annotation = MKPointAnnotation()
annotation.title = location.title
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
uiView.addAnnotation(annotation)
}
if messpunkt_manager.karteAusgeaehlt == 0 {
uiView.mapType = .standard
}
if messpunkt_manager.karteAusgeaehlt == 1 {
uiView.mapType = .hybrid
}
if messpunkt_manager.karteAusgeaehlt == 2 {
uiView.mapType = .satellite
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
the center coordinates should then be put into this #EnvironmentObject variable
#Published var position = CLLocationCoordinate2D()
thanks for your help!
And sorry for my naming. For me its easier to name the variables, functions ... in my native language German. I hope its not a big deal.
In your class Coordinator you need to set the value. Assuming your coordinator is set up like this:
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
...
}
Then you would add this function:
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
parent. messpunkt_manager.position = mapView.region
}
It is really helpful to post a Minimal Reproducible Example (MRE). As you can see, you left out an important piece
I have custom Map with pin on user location. When i need to show user location. I tried use CLLocationManager to show coordinates but they turns 0.0
struct MapView : UIViewRepresentable {
func makeCoordinator() -> MapView.Coordinator {
return MapView.Coordinator(parent1: self)
}
#Binding var title : String
#Binding var subtitle : String
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let map = MKMapView()
let coordinate = CLLocationCoordinate2D(latitude: 55.757485,
longitude: 37.632179)
map.region = MKCoordinateRegion(center: coordinate,
latitudinalMeters: 100,
longitudinalMeters: 100)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
map.delegate = context.coordinator
map.addAnnotation(annotation)
return map
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {}
class Coordinator: NSObject , MKMapViewDelegate {
var parent : MapView
init(parent1: MapView) {
parent = parent1
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let pin = MKPinAnnotationView(annotation: annotation,
reuseIdentifier: "pin")
pin.isDraggable = true
pin.pinTintColor = .blue
pin.animatesDrop = true
return pin
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationView.DragState, fromOldState oldState: MKAnnotationView.DragState) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: (view.annotation?.coordinate.latitude)!,
longitude: (view.annotation?.coordinate.longitude)!)){ (places, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
self.parent.title = (places?.first?.name ?? places?.first?.postalCode)!
self.parent.subtitle = (places?.first?.locality ?? places?.first?.country ?? "None")
}
}
}
}
There is my start point
let coordinate = CLLocationCoordinate2D(latitude: 55.757485,
longitude: 37.632179)
So i need to add user coordinates in this stroke without asking permit as i have already asked it
All you have to do is make the showsUserLocation var true.(Look at the docs)
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let map = MKMapView()
let coordinate = CLLocationCoordinate2D(latitude: 55.757485, longitude: 37.632179)
map.region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 100, longitudinalMeters: 100)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
// new code...
map.showsUserLocation = true
map.delegate = context.coordinator
map.addAnnotation(annotation)
return map
}
As of iOS 14 you can use the native SwiftUI Map. This might be easier to work with than bridging to UIKit
I'm getting the error:
Type 'NavView' does not conform to protocol 'UIViewRepresentable'
Here is my code. What should I do? Thanks. Using SwiftUI
struct NavView: UIViewRepresentable{
func makeUIView(context: Context) -> NavigationViewController {
var viewController: NavigationViewController
// Define two waypoints to travel between
let origin = Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.9131752, longitude: -77.0324047), name: "Mapbox")
let destination = Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.8977, longitude: -77.0365), name: "White House")
// Set options
let options = NavigationRouteOptions(waypoints: [origin, destination])
// Request a route using MapboxDirections.swift
Directions.shared.calculate(options) { (waypoints, routes, error) in
guard let route = routes?.first else { return }
// Pass the generated route to the the NavigationViewController
viewController = NavigationViewController(for: route)
viewController.modalPresentationStyle = .fullScreen
}
return viewController
}
func updateUIView(_ uiView: NavigationViewController, context: Context) {
}
}
For this case it should be used UIViewControllerRepresentable, like below
struct NavView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> NavigationViewController {
var viewController: NavigationViewController
// Define two waypoints to travel between
let origin = Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.9131752, longitude: -77.0324047), name: "Mapbox")
let destination = Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.8977, longitude: -77.0365), name: "White House")
// Set options
let options = NavigationRouteOptions(waypoints: [origin, destination])
// Request a route using MapboxDirections.swift
Directions.shared.calculate(options) { (waypoints, routes, error) in
guard let route = routes?.first else { return }
// Pass the generated route to the the NavigationViewController
viewController = NavigationViewController(for: route)
viewController.modalPresentationStyle = .fullScreen
}
return viewController
}
func updateUIViewController(_ uiViewController: NavigationViewController, context: Context) {
}
}