SwiftUI: How to put NavigationLink in MapPin or MapMarker - swift

I have already decode my JSON API and successfully display the location on the map with MapAnnotations and put NavigationLink to see the detail on it. But somehow, when I zoomed out the map to see all marked locations, suddenly my view becomes very laggy with simulator and real iPhone 8 (maybe because I have 100+ annotations on the map?). And then I tried to use MapMarker and the view becomes more smoother, but the problem is now I can't put NavigationLink on MapMarker as well as MapPin. Is there a proper way to display marker/annotations on the map and NavigationLink without making the view lag??
Here is my LocationManager Code to track user's location
import Foundation
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
#Published var location: CLLocation?
override init() {
super.init()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
locationManager.stopUpdatingLocation()
DispatchQueue.main.async {
self.location = location
}
}
}
My ContentView to display the Map and Show the annotations
import SwiftUI
import MapKit
import Combine
struct ContentView: View {
var body: some View {
NavigationView{
ServiceLocation()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension MKCoordinateRegion {
static var defaultRegion: MKCoordinateRegion {
MKCoordinateRegion(center: CLLocationCoordinate2D.init(latitude: -0.789275, longitude: 113.921327), latitudinalMeters: 5000, longitudinalMeters: 5000)
}
}
//MARK: MAP VIEW
struct ServiceLocation: View{
#State var serviceLocations: [ServiceLocationJSON] = []
#ObservedObject private var locationManager = LocationManager()
#State private var region = MKCoordinateRegion.defaultRegion
#State private var cancellable: AnyCancellable?
private func setCurrentLocation() {
cancellable = locationManager.$location.sink { location in
region = MKCoordinateRegion(center: location?.coordinate ?? CLLocationCoordinate2D(), latitudinalMeters: 20000, longitudinalMeters: 20000)
}
}
var body: some View{
GeometryReader{ geometry in
VStack{
if locationManager.location != nil {
Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true, userTrackingMode: .none, annotationItems: serviceLocations) { location in
MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: location.LATITUDE, longitude: location.LONGITUDE)){
NavigationLink(destination: serviceLocationDetail(serviceLocations: location)){
Image(systemName: "mappin")
.resizable()
.scaledToFit()
.frame(width: geometry.size.width / 15, height: geometry.size.height / 15)
}
}
}
} else {
VStack{
Spacer()
ProgressView()
Spacer()
}
}
}.onAppear{
setCurrentLocation()
getServiceLocation(url: "https://my.api.mockaroo.com/latlong.json?key=e57d0e40"){ (serviceLocations) in
self.serviceLocations = serviceLocations
}
}
.navigationTitle("Service")
.navigationBarTitleDisplayMode(.inline)
}
}
}
//MARK: DETAIL VIEW
struct serviceLocationDetail: View{
var serviceLocations: ServiceLocationJSON
var body: some View{
VStack{
if serviceLocations.DEALER_NAME.isEmpty{
VStack{
Spacer()
ProgressView()
Spacer()
}
}else{
VStack(alignment: .leading, spacing: 10){
Text(serviceLocations.DEALER_NAME)
.fontWeight(.medium)
.padding(.leading, 10)
Text(serviceLocations.DEALER_ADDRESS)
.padding(.leading, 10)
HStack(spacing: 5){
Image(systemName: "phone.fill")
Text(serviceLocations.PHONE)
}.padding(.leading, 10)
Spacer()
}.navigationBarTitle(serviceLocations.DEALER_NAME)
}
}
Spacer()
}
}
//MARK: JSON MODEL
struct ServiceLocationJSON: Identifiable, Decodable{
var id: Int
var LATITUDE: Double
var LONGITUDE: Double
var DEALER_NAME: String
var DEALER_ADDRESS: String
var DEALER_PICTURE: String
var PHONE: String
}
//MARK: DECODE JSON MODEL
func getServiceLocation(url: String, completion: #escaping ([ServiceLocationJSON])->()){
let session = URLSession(configuration: .default)
session.dataTask(with: URL(string: url)!){ (data, _, err) in
if err != nil{
print(err!.localizedDescription)
return
}
do{
let serviceLocations = try
JSONDecoder().decode([ServiceLocationJSON].self, from: data!)
completion(serviceLocations)
}
catch{
print(error)
}
}.resume()
}
Build with Xcode 12 and Swift 5

Nevermind, I just solved it. I just need to change coordinateRegion: $region to coordinateRegion: .constant(region)

Related

How to find UUID from AirTag Beacons

I'm trying to make an AirTag Detector to my Final's Papers in SwiftUI, but I can't find them because they don't have any ID, just his Serial Number and I can't use that. Somehow I can create an app to simulate "Find app" from Apple or find UUID from IT?
If UUID doesn't work, have another way to do that?
I saw in this URL maybe we can't get AirTag's ID.
Is that true?
https://forums.macrumors.com/threads/airtags-bluetooth-id-tracking-explained.2294592/
import Combine
import CoreLocation
import SwiftUI
class BeaconDetector: NSObject, ObservableObject, CLLocationManagerDelegate
{
var didChange = PassthroughSubject<Void, Never>()
var locationManager : CLLocationManager?
var lastDistance = CLProximity.unknown
override init(){
super.init()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestWhenInUseAuthorization()
}
func locationManager( _ manager:CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus){
if status == .authorizedWhenInUse {
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self){
if CLLocationManager.isRangingAvailable() {
startScanning()
}
}
}
}
func startScanning() {
let uuid = UUID(uuidString: "d31bb872-27f2-11ed-a261-0242ac120002")! //Doesn't exist this UUID
let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: 69, minor: 690)
let beaconRegion = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "Home")
locationManager?.startMonitoring(for: beaconRegion)
locationManager?.startRangingBeacons(satisfying: constraint)
}
func locationManager(_ manager: CLLocationManager, didRange beacons:
[CLBeacon], satisfying beaconConstraint:
CLBeaconIdentityConstraint) {
if let beacon = beacons.first {
update(distance: beacon.proximity)
} else {
update(distance: .unknown)
}
}
func update(distance: CLProximity) {
lastDistance = distance
didChange.send(())
}
}
struct BigText: ViewModifier {
func body (content: Content) -> some View {
content
.font(Font.system(size: 72, design: .rounded))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}
struct ContentView: View {
#ObservedObject var detector = BeaconDetector()
var body: some View {
if detector.lastDistance == .immediate {
return Text("RIGHT HERE")
.modifier(BigText())
.background(Color.red)
.edgesIgnoringSafeArea(.all)
} else if detector.lastDistance == .near {
return Text("NEAR")
.modifier(BigText())
.background(Color.orange)
.edgesIgnoringSafeArea(.all)
} else if detector.lastDistance == .far {
return Text("FAR")
.modifier(BigText())
.background(Color.blue)
.edgesIgnoringSafeArea(.all)
} else {
return Text("UNKNOWN")
.modifier(BigText())
.background(Color.gray)
.edgesIgnoringSafeArea(.all)
}
}
}

How can I add the beacon id?

I would like to show on the screen the distance (Unknown, far, near or right here) and the ID of the found beacon. This is my code
struct ContentView: View{
#ObservedObject var detector = BeaconDetector()
var body: some View {
if detector.lastDistance == .immediate {
return Text("RIGHT HERE")
// id here
.modifier(BigText())
.background(Color.green)
.edgesIgnoringSafeArea(.all)
} else if detector.lastDistance == .near {
return Text("NEAR")
.modifier(BigText())
.background(Color.orange)
.edgesIgnoringSafeArea(.all)
} else if detector.lastDistance == .far {
return Text("FAR")
.modifier(BigText())
.background(Color.red)
.edgesIgnoringSafeArea(.all)
} else {
return Text("UNKNOWN")
.modifier(BigText())
.background(Color.gray)
.edgesIgnoringSafeArea(.all)
}
}
}
import Combine
import CoreLocation
import SwiftUI
class BeaconDetector: NSObject, ObservableObject,
CLLocationManagerDelegate{
var didChange = PassthroughSubject<Void, Never>()
var locationManager: CLLocationManager?
var beaconID = UUID().self
#Published var lastDistance = CLProximity.unknown
override init() {
super.init()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
if CLLocationManager.isMonitoringAvailable(for:
CLBeaconRegion.self){
if CLLocationManager.isRangingAvailable(){
startScanning()
}
}
}
}
func startScanning() {
let uuid = UUID(uuidString: "D3D6736B-4C7C-412D-865B-EE61ACF88C61")!
let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: 123,
minor: 456)
let beconRegion = CLBeaconRegion(beaconIdentityConstraint:
constraint, identifier: "MyBeacon")
locationManager?.startMonitoring(for: beconRegion)
locationManager?.startRangingBeacons(satisfying: constraint)
}
func locationManager(_ manager: CLLocationManager, didRange beacons:
[CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
if let beacon = beacons.first {
update(distance: beacon.proximity)
} else {
update(distance: .unknown)
}
}
func update(distance: CLProximity) {
lastDistance = distance
didChange.send(())
}
}
struct BigText: ViewModifier {
func body(content: Content) -> some View {
content
.font(Font.system(size: 72, design: .rounded))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0,
maxHeight: .infinity)
}
}
Try something like this:
Text("Beacon with major: \(detector.lastMajor) minor: \(detector.lastMinor) is RIGHT HERE")
.modifier(BigText())
.background(Color.green)
.edgesIgnoringSafeArea(.all)
If the BeaconDetector class has a field for lastMajor and lastMinor you can reference these inside your string to be shown on the screen by escaping the expression inside the string with \\().
Since the clarified code shows that the major and minor are not fields on BeaconDetector you will need to add them like this:
#Published var lastMajor: NSNumber? = nil
#Published var lastMinor: NSNumber? = nil
And then you will need to update them when you update the proximity like this:
update(distance: beacon.proximity, major: beacon.major, minor: beacon.minor)
And add these to your update function like this:
func update(distance: CLProximity, major: NSNumber, minor: NSNumber) {
lastDistance = distance
lastMajor = major
lastMinor = minor
didChange.send(())
}

Swiftui photoOutput for Images not called every time

This is actually the same issue as this post AVCapturePhotoCaptureDelegate photoOutput() not called every time however no one responded to that . I find that on takePic the photoOutput function is called sometimes and not others it is literally 50/50 . I am using Swiftui 2.0 . Does anyone know a work around this or why this issue is happening ? The code to replicate this is actually quite small .It is the code below and then setting permissions in the info.plist for Privacy - Camera usage description and privacy - photo library usage description . I have tried different things and it is literally still a 50/50 on whether photoOutput gets called . When it is not called you will see this in log print("Nil on SavePic:picData") Any suggestions would be great .
import SwiftUI
import AVFoundation
struct CreateStoryView: View {
#StateObject var camera = CameraModel()
#Environment(\.presentationMode) var presentationMode
var body: some View {
ZStack {
// Going to be Camera Preview
CameraPreview(camera: camera)
.ignoresSafeArea(.all, edges: .all)
VStack {
HStack {
Spacer()
Image(systemName: "arrowshape.turn.up.backward.circle.fill")
.foregroundColor(.black)
.padding(.trailing,20)
.background(Color.white)
.clipShape(/*#START_MENU_TOKEN#*/Circle()/*#END_MENU_TOKEN#*/)
.onTapGesture {
if camera.session.isRunning == true {
camera.session.stopRunning()
}
self.presentationMode.wrappedValue.dismiss()
}
if camera.isTaken {
Button(action: camera.reTake, label: { // camera.reTake
Image(systemName: "arrow.triangle.2.circlepath.camera")
.foregroundColor(.black)
.padding()
.background(Color.white)
.clipShape(/*#START_MENU_TOKEN#*/Circle()/*#END_MENU_TOKEN#*/)
})
.padding(.trailing,10)
}
}
Spacer()
HStack{
// If Taken then showing save and again take button
if camera.isTaken{
Button(action: {if !camera.isSaved{camera.savePic()}}, label: {
Text(camera.isSaved ? "Saved" : "Save")
.foregroundColor(.black)
.fontWeight(.semibold)
.padding(.vertical,10)
.padding(.horizontal,20)
.background(Color.white)
.clipShape(Capsule())
})
.padding(.leading)
Spacer()
} else {
Button(action: camera.takePic , label: {
ZStack{
Circle()
.fill(Color.white)
.frame(width: 65, height: 65)
Circle()
.stroke(Color.white,lineWidth: 2)
.frame(width: 75, height: 75)
}
})
}
}.frame(height: 75)
}
}.onAppear(perform: {
camera.Check()
})
}
}
// Camera Model
class CameraModel: NSObject,ObservableObject,AVCapturePhotoCaptureDelegate {
#Published var isTaken = false
#Published var session = AVCaptureSession()
#Published var alert = false
#Published var output = AVCapturePhotoOutput()
// preview ...
#Published var preview: AVCaptureVideoPreviewLayer!
#Published var isSaved = false
#Published var picData = Data(count: 0)
func Check() {
// first checking camera has permission
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
return
case .notDetermined:
//retrusting for permission
AVCaptureDevice.requestAccess(for: .video) {(status) in
if status{
self.setUp()
}
}
case .denied:
self.alert.toggle()
return
default:
return
}
}
func setUp() {
// setting up camera
do{
// setting configs...
self.session.beginConfiguration()
// change for your own
let device = AVCaptureDevice.default(.builtInDualCamera,for: .video,position: .back)
let input = try AVCaptureDeviceInput(device: device!)
// checking and adding session
if self.session.canAddInput(input) {
self.session.addInput(input)
}
// same for output
if (self.session.canAddOutput(self.output)) {
self.session.addOutput(self.output)
}
self.session.commitConfiguration()
} catch {
print(error.localizedDescription)
}
}
// take and retake
func takePic(){
DispatchQueue.global(qos: .background).async {
self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
self.session.stopRunning()
DispatchQueue.main.async {
withAnimation{ self.isTaken.toggle() }
}
}
}
func reTake() {
DispatchQueue.global(qos: .background).async {
self.session.startRunning()
DispatchQueue.main.async {
// withAnimation{ self.isTaken.toggle() }
// clearing
//self.isSaved = false
self.isTaken = false
}
}
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
print("photoOutput check")
if error != nil {
return
}
guard var imageData = photo.fileDataRepresentation() else {return}
self.picData = imageData
if isSaved == true {
if !imageData.isEmpty {
imageData.removeAll()
isSaved = false
}
} else {
isSaved = true
}
}
func savePic() {
if UIImage(data: self.picData) == nil {
print("Nil on SavePic:picData")
return
}
let image = UIImage(data: self.picData)!
// saving image
UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveError), nil)
self.isSaved = true
print("saved sucessfully")
}
#objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
print("Save finished!")
}
}
// setting up view for preview
struct CameraPreview: UIViewRepresentable {
#ObservedObject var camera: CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
struct CreateStoryView_Previews: PreviewProvider {
static var previews: some View {
CreateStoryView()
}
}

SwiftUI Showing Specific Landmarks on Opening the map

I was wondering to see if there is a way to show the nearest specific landmarks[ie railway stations] just to see where the user can go to? I am trying to also show the point to point of landmarks but should revolve first around the user. top 3 nearest landmarks should do, please any help or resources?
import MapKit
import SwiftUI
import CoreLocation
struct SearchView: View {
var body: some View {
Home()
}
}
struct SearchView_Previews: PreviewProvider {
static var previews: some View {
SearchView()
}
}
struct Home: View {
#State var map = MKMapView()
#State var manager = CLLocationManager()
#State var alert = false
#State var source : CLLocationCoordinate2D!
#State var destination : CLLocationCoordinate2D!
var body: some View{
ZStack(alignment: .bottom){
VStack(spacing:0){
HStack{
Text("Station Search").font(.title)
Spacer()
}
.padding()
.padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top).background(Color.white)
MapView(map: self.$map, manager: self.$manager, alert:
self.$alert, source: self.$source,
destination: self.$destination)
.onAppear {
self.manager.requestAlwaysAuthorization()
}
}
}.edgesIgnoringSafeArea(.all)
.alert(isPresented: self.$alert) { () -> Alert in
Alert(title: Text("Error"), message: Text("Please Enable Location In Setting !!!"), dismissButton: .destructive(Text("Ok")))
}
}
}
struct MapView: UIViewRepresentable {
#Binding var map: MKMapView
#Binding var manager : CLLocationManager
#Binding var alert : Bool
#Binding var source : CLLocationCoordinate2D!
#Binding var destination : CLLocationCoordinate2D!
func makeCoordinator() -> Coordinator {
return MapView.Coordinator(parent1: self)
}
func makeUIView(context: Context) -> MKMapView {
map.delegate = context.coordinator
manager.delegate = context.coordinator
map.showsUserLocation = true
return map
}
func updateUIView(_ uiView: MKMapView, context: Context){
}
class Coordinator: NSObject, MKMapViewDelegate, CLLocationManagerDelegate {
var parent : MapView
init(parent1 : MapView) {
parent = parent1
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .denied {
self.parent.alert.toggle()
}
else{
self.parent.manager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations:[CLLocation]) {
let region = MKCoordinateRegion(center: locations.last!.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
self.parent.source = locations.last!.coordinate
self.parent.map.region = region
}
}
}

How to get access to underlying GMSMapView from UIRepresentable MapView

In my app I have a ViewModel(MapViewModel) class, a UIRepresentable class and ContentView. I am looking for a way to get access to GMSMapView view in the ViewModel that is created as a UIRepresentable class.
ContentView.swift:
import SwiftUI
import Combine
struct ContentView: View {
#State private var selection = 0
#State private var dragOffset = CGSize.zero
#ObservedObject var mapViewModel : MapViewModel = MapViewModel()
var body: some View {
GeometryReader { geo in
TabView(selection: self.$selection) {
MapView()
.edgesIgnoringSafeArea(.all)
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}
.tag(0)
Text("Second Page")
.tabItem {
VStack {
Image(systemName: "gear")
Text("Settings")
}
}
.tag(1)
Text("Third Page")
.tabItem {
VStack {
Image(systemName: "gear")
Text("Third Page")
}
}
.tag(2)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
MapViewModel.swift:
import Foundation
import Combine
import GoogleMaps
import os
class MapViewModel: NSObject, ObservableObject {
let lm = CLLocationManager()
var myLocations = [CLLocation]()
override init() {
super.init()
lm.delegate = self
lm.desiredAccuracy = kCLLocationAccuracyBest
lm.requestWhenInUseAuthorization()
lm.pausesLocationUpdatesAutomatically = false
lm.allowsBackgroundLocationUpdates = true
lm.startUpdatingLocation()
}
}
extension MapViewModel: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
//self.status = status
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
os_log("locationManager:didUpdateLocations: received location",log: Log.general, type: .debug)
}
}
MapView.swift:
import UIKit
import SwiftUI
import GoogleMaps
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: 30.267153, longitude: -97.7430608, zoom: 6.0)
let gmsMapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
gmsMapView.delegate = context.coordinator
return gmsMapView
}
func updateUIView(_ mapView: GMSMapView, context: Self.Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, GMSMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
}
}
struct GoogleMapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}
So any idea on how I can access the gmsMapView object in MapViewModel. I need access to draw lines on the map...Thanks!
I was able to get past this issue by defining the polyline overly value as #Published and accessing it via viewmodel in updateUI. So instead of trying to access the mapview from the viewmodel, I let the view add the polyline overlay. Hope this helps someone else. Thanks
ContentView.swift:
struct ContentView: View {
#State private var selection = 0
#State private var dragOffset = CGSize.zero
#ObservedObject var mapViewModel: MapViewModel
var body: some View {
GeometryReader { geo in
TabView(selection: self.$selection) {
MapView()
.edgesIgnoringSafeArea(.all)
.tabItem {
VStack {
Image(systemName: "house")
Text("Home")
}
}
.tag(0)
Text("Second Page")
.tabItem {
VStack {
Image(systemName: "gear")
Text("Settings")
}
}
.tag(1)
Text("Third Page")
.tabItem {
VStack {
Image(systemName: "gear")
Text("Third Page")
}
}
.tag(2)
}
}
}
}
MapView:
struct MapView: UIViewRepresentable {
#ObservedObject var mapViewModel = MapViewModel()
func makeUIView(context: Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: 30.5986015, longitude: -97.8210401, zoom: 20.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.isMyLocationEnabled = true
mapView.animate(toViewingAngle: 45)
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Self.Context) {
if (mapViewModel.polyline != nil) {
print("updateUIView: Polyline = \(mapViewModel.polyline!.description)")
os_log("updateUIView: Polyline = %{Public}s",log: Log.general, type: .debug, mapViewModel.polyline!.description)
mapViewModel.polyline!.strokeColor = UIColor.red
mapViewModel.polyline!.strokeWidth = 5.0
mapViewModel.polyline!.map = mapView
}
if (mapViewModel.locChanged && mapViewModel.myLocations.count > 0) {
print("updateUIView: Refocus camera on last location")
os_log("updateUIView: Refocus camera on last location",log: Log.general, type: .debug, mapViewModel.polyline!.description)
let camera = GMSCameraPosition.camera(withLatitude: (mapViewModel.myLocations.last?.coordinate.latitude)!, longitude: (mapViewModel.myLocations.last?.coordinate.longitude)!, zoom: 20.0)
let _ = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, GMSMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
}
}
MapViewModel:
class MapViewModel: NSObject, ObservableObject {
.
.
.
#Published var polyline: GMSPolyline?
#Published var locChanged: Bool = false
override init() {
super.init()
.
.
.
polyline = nil
}
.
.
.
func draw(myLocations: [CLLocation], color: UIColor) {
os_log("MapViewController: Drawing Track for last two Locations",log: Log.general, type: .info)
print("MapViewController: Drawing Track for last two Locations")
let path = GMSMutablePath()
let c1 = myLocations[myLocations.count - 1].coordinate
let c2 = myLocations[myLocations.count - 2].coordinate
path.add(c1)
path.add(c2)
polyline = GMSPolyline(path: path)
print("draw: Polyline = \(polyline!.description)")
polyline!.strokeColor = color
polyline!.strokeWidth = 5.0
}
.
.
.
extension MapViewModel: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
os_log("locationManager:didUpdateLocations: received location",log: Log.general, type: .debug)
guard let lastLocation: CLLocation = locations.last else {
os_log("locationManager:didUpdateLocations: received null location",log: Log.general, type: .debug)
return
}
if (myLocations.count >= 2) {
os_log("MapViewController: updateLocation: Calling draw method with count = %{Public}s",log: Log.general, type: .debug, myLocations.count.description)
self.draw(myLocations: self.myLocations, color:.red)
}
.
.
.
} else {
os_log("LocationManager: Bad Location...",log: Log.general, type: .error)
badLocationCount += 1
locChanged = false
}
}
}