I'm trying to show circleOverlays on an MKMapView however it isn't showing up

86 Views Asked by At

The code below is to take a specific number of references from a database, make circle overlays from some coordinates given from some DBSCAN function, and plot the circle overlays on the map. Every 10 seconds the circles are removed from the map and updated with the same ones from the database. The problem is that the circles aren't being shown. I have tried debugging this to no avail. Any advice/help is appreciated.

import MapKit
import SwiftUI
import Firebase
import FirebaseAnalytics
import FirebaseAnalyticsSwift

struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
/* Variable region accounts for showing specific coordinate region within span, showcasing input coordinates */
    @State var annotationItems: [User] = []
    @State var locations: [CLLocation] = []
    @State var circleOverlays: [MKCircle] = []
    
    func plotUsers() {
        for i in 0...20
        {
            var latData = 0.0
            var longData = 0.0
            var userName = ""
            var userAge = 0
            var ref: DatabaseReference!

            ref = Database.database().reference().child("users").child(String(i))
            ref.getData(completion:  { error, snapshot in
                guard error == nil else {
                    print("issue")
                    return;
                }
                var a: [String: Any] = [:]
                //turning datasnapshot returned from database into a dictionary
                a = snapshot?.value as! Dictionary<String, Any>
                //assigning values from the dictionary to variables so we don't have to type all the necessary error stuff every time
                latData = (a["locData"] as? [String:Any])?["lat"] as? Double ?? -1
                longData = (a["locData"] as? [String:Any])?["long"] as? Double ?? -1
                userName = (a["name"]) as! String
                userAge = Int((a["age"] as? [String:Any])?["age"] as? String ?? "-1") ?? -5
                annotationItems.append(User(name:userName,age: userAge,latitude: latData,longitude: longData))
                self.locations.append(CLLocation(latitude: latData, longitude: longData))
            });
        }
        print("@@@@@@@@@@@@@@@@@@@@@@@Locations:@@@@@@@@@@@@@@@@@@@@@@@@",locations.count)
        
        let dbscan = DBSCAN(self.locations)
        let (sequence, places) = dbscan.findCluster(eps: 1000.0, minPts: 2)
        print("@@@@@@@@@@@@@@@@@@@@@@@Places:@@@@@@@@@@@@@@@@@@@@@@@@",places)
    
        for place in places {
            print("Cluster:", place.members.count)
            let circle = MKCircle(center: place.location.coordinate, radius: 10000)
            self.circleOverlays.append(circle)
        }
        self.viewModel.mapView.addOverlays(circleOverlays)
        print("@@@@@@@@@@@@@@@@@@@@@@@Circles:@@@@@@@@@@@@@@@@@@@@@@@@",circleOverlays)
        
    }
    
    private var timer: Timer?
    
    var body: some View {
        Map(coordinateRegion: $viewModel.region,
            showsUserLocation: true)
        .onAppear() {
//            viewModel.mapView.delegate = viewModel
            let _ = self.plotUsers()
//            let _ = self.plotUsers()
            timer?.invalidate()
            Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in
                let _ = self.plotUsers()
                self.locations.removeAll()
                self.viewModel.mapView.removeOverlays(circleOverlays)
                self.circleOverlays.removeAll()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider { /* Shows map */
    static var previews: some View {
        ContentView()
    }
}

final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate, MKMapViewDelegate {
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(
                                                   latitude: 38.898022, longitude: -77.050604),
                                                   span: MKCoordinateSpan(
                                                   latitudeDelta: 0.05, longitudeDelta: 0.05)) /* Can substitute region coordinates with longitude,latitude from firebase */

    @Published var map = MKMapView()
    var locationManager: CLLocationManager? /* Enables location services*/
    let mapView: MKMapView!
    
    override init() {
        mapView = MKMapView()
        super.init()
        mapView.delegate = self
    }
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let circleOverlay = overlay as? MKCircle {
            let circleRenderer = MKCircleRenderer(circle: circleOverlay)
            circleRenderer.fillColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.3)
            circleRenderer.strokeColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.7)
            circleRenderer.lineWidth = 2.0
            return circleRenderer
        }
        return MKOverlayRenderer()
    }
}

Im new to Swift so Im not entirely sure if the $viewModel.region should somehow be set to the MKMapView created in the ContentViewModel class.

1

There are 1 best solutions below

0
llamahairy On

Here is MKMapView with UIViewRepresentable if you decide to go that route. This places a circle wherever the user taps on the map by adding MKCircles to an array. Not totally what you're looking for - I wasn't sure how to incorporate and test your database function - but hopefully this helps in some way to see one option:

import SwiftUI
import CoreLocation
import MapKit

struct MapTestQ2: UIViewRepresentable {
    @Binding var centerCoordinate: CLLocationCoordinate2D
    @State private var mapView = MKMapView()
    @State private var circles: [MKCircle] = []
   
    func makeUIView(context: Context) -> MKMapView {
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = true
        let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
        let region = MKCoordinateRegion(center: centerCoordinate, span: span)
        mapView.setRegion(region, animated: true)
        return mapView
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        mapView.addOverlays(circles)
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    
    class Coordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
        @State var parent: MapTestQ2
        var tRecognizer = UITapGestureRecognizer()
        
        init(_ parent: MapTestQ2) {
            self.parent = parent
            super.init()
            self.tRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
            self.tRecognizer.delegate = self
            self.parent.mapView.addGestureRecognizer(tRecognizer)
        }
        
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            guard let circleoverlay = overlay as? MKCircle else {return MKOverlayRenderer()}
            let circleRenderer = MKCircleRenderer(circle: circleoverlay)
            circleRenderer.strokeColor = .blue
            circleRenderer.fillColor = .systemCyan
            circleRenderer.alpha = 0.5
            return circleRenderer
          
        }
        
        @objc func tapHandler(_ gesture: UITapGestureRecognizer) {
            print("Tapped")
            let location = tRecognizer.location(in: self.parent.mapView)
            let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
            let acircle = MKCircle(center: coordinate, radius: 500.0)
            self.parent.circles.append(acircle)
        }
    }
}

struct SEQ2: View {
    @State private var testcoord = CLLocationCoordinate2D(latitude: 37.33461, longitude: -122.00898)
    var body: some View {
                   MapTestQ2(centerCoordinate: $testcoord)
    }
}