I am working on a project that gathers real-time location data and stores it in a database (using google firebase). I am relatively new to Swift but have been coding for about 2 years. I have created a basic HomePage UI and a MapView UI, using a navigation link to move from page to page. The MapViewModel class is a class used to gather location data from the user. I am trying to call a method of the MapViewModel class with .onAppear. Although when I run the program the function is not called and "inside" isn't printed on the terminal. Am I missing something on how .onAppear and navigation links work? Please let me know what the best solution would be to call the function when the view is switched to MapView.
import SwiftUI
import MapKit
struct HomePage: View {
@State private var viewModel = MapViewModel()
var body: some View {
NavigationView {
ZStack {
LinearGradient(
gradient: Gradient(colors: [.blue, Color(red:0.18, green: 0.79, blue: 0.91, opacity: 1.0)]),
startPoint: .topLeading,
endPoint: .bottomTrailing)
.edgesIgnoringSafeArea(.all)
NavigationLink {
MapView()
}
label: {
ZStack {
Circle()
.trim(from: 0.5)
.frame(width: 450, height: 450)
.foregroundColor(Color(red: 1.0, green: 0.89, blue: 0.36, opacity: 1.0))
Text("Navigate")
.foregroundColor(.white)
.font(.system(size:32, weight: .bold, design: .default))
.padding(.bottom, 280)
}
.position(x: 195, y: 900)
.ignoresSafeArea(.all)
}
}
}
}
}
struct MapView: View {
@State private var viewModel = MapViewModel()
var body: some View {
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.ignoresSafeArea()
.accentColor(Color(.systemPink))
.onAppear {
print("inside")
viewModel.checkIfLocationServiceIsEnabled()
}
}
}
final class MapViewModel : NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33, longitude: -120), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
var locationManager: CLLocationManager?
func checkIfLocationServiceIsEnabled() {
if CLLocationManager.locationServicesEnabled() {
locationManager = CLLocationManager()
locationManager!.startUpdatingLocation()
locationManager!.delegate = self
}
else {
print("Print error message")
}
}
func checkLocationAuthorization() {
guard let locationManager = locationManager else { return }
switch locationManager.authorizationStatus {
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
print("Location restricted")
case .denied:
print("You have denied location permission, go to settings")
case .authorizedAlways, .authorizedWhenInUse:
print("Inside")
if locationManager.location?.coordinate != nil {
region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
}
else {
print("Location not found")
}
@unknown default:
break
}
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
checkLocationAuthorization()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
print(lastLocation)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
HomePage()
}
}
CodePudding user response:
Use @StateObject var viewModel = MapViewModel()
in your HomePage
view. Add .environmentObject(viewModel)
to your NavigationView
and the corresponding @EnvironmentObject var viewModel: MapViewModel
in MapView
. Works for me.
This is the code I used for my tests, that works for me.
import Foundation
import SwiftUI
import MapKit
struct ContentView: View {
var body: some View {
HomePage()
}
}
struct HomePage: View {
@StateObject var viewModel = MapViewModel() // <-- here
var body: some View {
NavigationView {
ZStack {
LinearGradient(
gradient: Gradient(colors: [.blue, Color(red:0.18, green: 0.79, blue: 0.91, opacity: 1.0)]),
startPoint: .topLeading,
endPoint: .bottomTrailing)
.edgesIgnoringSafeArea(.all)
NavigationLink {
MapView()
}
label: {
ZStack {
Circle()
.trim(from: 0.5)
.frame(width: 450, height: 450)
.foregroundColor(Color(red: 1.0, green: 0.89, blue: 0.36, opacity: 1.0))
Text("Navigate")
.foregroundColor(.white)
.font(.system(size:32, weight: .bold, design: .default))
.padding(.bottom, 280)
}
.position(x: 195, y: 900)
.ignoresSafeArea(.all)
}
}
}.environmentObject(viewModel) // <-- here
}
}
struct MapView: View {
@EnvironmentObject var viewModel: MapViewModel // <-- here
var body: some View {
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.ignoresSafeArea()
.accentColor(Color(.systemPink))
.onAppear {
print("\n----> in onAppear \n")
viewModel.checkIfLocationServiceIsEnabled()
}
}
}
final class MapViewModel : NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33, longitude: -120), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
func checkIfLocationServiceIsEnabled() {
print("\n----> in checkIfLocationServiceIsEnabled")
}
}
If that does not work with your system, try this:
struct MapView: View {
@EnvironmentObject var viewModel: MapViewModel
var body: some View {
VStack { // <-- here
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.ignoresSafeArea()
.accentColor(Color(.systemPink))
}
.onAppear {
print("\n----> in onAppear \n")
viewModel.checkIfLocationServiceIsEnabled()
}
}
}