Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Package not recognised #29

Closed
ahartman opened this issue Dec 3, 2022 · 18 comments
Closed

Package not recognised #29

ahartman opened this issue Dec 3, 2022 · 18 comments

Comments

@ahartman
Copy link

ahartman commented Dec 3, 2022

Dear Paul,

I added your package 'Map' to my project as explained by Apple.
My project has a working map based on MapKit but I want to use extended facilities based on your package.
It is visible as a Package dependency as well as listed under'Frameworks, Libraries and embedded Content'.

However it seems not to be recognised by Xcode as I get 'Cannot find type 'MKCoordinateRegion' in scope and similar errors for MyLocation, MKDirections and UserTrackingMode.

Any ideas how to resolve this.
Kind regards, ahartman, belgium

@pauljohanneskraft
Copy link
Owner

MKCoordinateRegion is not defined in this package but rather in MapKit - so you will need to put import MapKit at the top of the file to use MapKit-defined APIs. We might want to change this in the future though, since it would make sense to simply re-export the MapKit interface as well.

@ahartman
Copy link
Author

ahartman commented Dec 4, 2022

Thank you for the quick reply. It would be good to update the example code given in REEDME as this does not show importing Mapkit.
Kind regards, ahartman, belgium

@ahartman ahartman closed this as completed Dec 4, 2022
@ahartman ahartman reopened this Dec 4, 2022
@ahartman
Copy link
Author

ahartman commented Dec 4, 2022

Dear Paul,
I am struggling with two things and I am not very good in reading code to find how to use it:

  1. I want to draw 3 concentric circles with different radiuses and colours from the center of the map but I cannot adapt the overlay example code to work. Can you help a bit here?
  2. I also wanted to use MapZoomControl to enable zooming on my Mac; however, I get an error: "Cannot find 'MapZoomControl' in scope" in the code below:
var body: some View {
        Map(coordinateRegion: $region,
            annotationItems: mainModel.mapElementLines,
            annotationContent: {
            mapLocation in
            MapPin(coordinate: mapLocation.coordinate!, tint: mapLocation.color)}
        )
        .mapKey(1)
        .overlay(alignment: .topLeading) {
            MapZoomControl(key: 1)
        }
    }

Any ideas?

Kind regards, ahartman, belgium

@pauljohanneskraft
Copy link
Owner

Hey, sorry for responding so late.

  1. If they should only be on top of the map regardless of coordinates, please simply use SwiftUI's overlay functionality.

For coordinate-fixed circles, you may want to use the following example:

struct MapOverlayCircle: Identifiable {
    let id = UUID()
    let center: CLLocationCoordinate2D
    let radius: CLLocationDistance
    let strokeColor: Color
}

struct MyMapView: View {

    @State private var overlays = [
        MapOverlayCircle(...),
        MapOverlayCircle(...),
    ]

    @State private var region = MKCoordinateRegion()    

    var body: some View {
        Map(
            coordinateRegion: $region,
            overlayItems: overlays
         ) { overlay in
               MapCircle(center: overlay.center, radius: overlay.radius, strokeColor: overlay.strokeColor)
         }
    }
}

This example will draw two circles on the map (the initializers would need to be filled instead of the ellipsis (...) of course).
For more customization, you might want to have a deeper look into MapCircle initializers.

  1. MapZoomControl is only available on Mac - maybe you try to build the app for an iOS target and then it fails?

For these cases, please make sure that MapZoomControl is only referenced in macOS code, for example using the following precompiler directives:

Map(
    coordinateRegion: $region,
    annotationItems: mainModel.mapElementLines
) { mapLocation in
   MapPin(coordinate: mapLocation.coordinate!, tint: mapLocation.color)}
)
.mapKey(1)
if os(macOS)
.overlay(alignment: .topLeading) {
    MapZoomControl(key: 1)
}
#endif

@ahartman
Copy link
Author

ahartman commented Feb 9, 2023

Paul,

Better a later answer than no answer, thank you for getting back to me.
I got your overlay example to work. However, I have no idea how to combine annotations AND circles on one map.
The code below of course displays two maps and I want to pins and the circles the same map.

The MapZoomControl is not visible with your code.

import SwiftUI
import MapKit
import Map

struct PatientsPinsCirclesView: View {
    static var home = CLLocationCoordinate2D(latitude: 51.35988, longitude: 4.48369)
    @EnvironmentObject var model: MainModel
    @State private var region = MKCoordinateRegion(
        center: home,
        span: MKCoordinateSpan(latitudeDelta: 0.6, longitudeDelta: 0.6)
    )
    @State private var overlays = [
        MapOverlayCircle(center: home, radius: 5000.0, strokeColor: .blue),
        MapOverlayCircle(center: home, radius: 10000.0, strokeColor: .green),
        MapOverlayCircle(center: home, radius: 15000.0, strokeColor: .red)
    ]

    var body: some View {
        VStack {
            Map(coordinateRegion: $region,
                annotationItems: model.mapPins,
                annotationContent: {
                mapPin in
                MapPin(coordinate: mapPin.coordinate, tint: mapPin.color)
            }
            )
            Map(coordinateRegion: $region,
                overlayItems: overlays) {
                overlay in
                MapCircle(center: overlay.center, radius: overlay.radius, strokeColor: UIColor(overlay.strokeColor))
            }
            .mapKey(1)
#if os(macOS)
            .overlay(alignment: .topLeading) {
                MapZoomControl(key: 1)
            }
#endif
        }
    }
}

struct PatientsPinsCirclesView_Previews: PreviewProvider {
    static var previews: some View {
        PatientsPinsCirclesView()
    }
}

struct MapOverlayCircle: Identifiable {
    let id = UUID()
    let center: CLLocationCoordinate2D
    let radius: CLLocationDistance
    let strokeColor: Color
}

Schermafbeelding 2023-02-09 om 12 22 01

@pauljohanneskraft
Copy link
Owner

You can provide overlays and annotations in the same initializer of a map

var body: some View {
    Map(
        coordinateRegion: $region,
        annotationItems: model.mapPins,
        annotationContent: { mapPin in
            MapPin(coordinate: mapPin.coordinate, tint: mapPin.color)
        },
        overlayItems: overlays,
        overlayContent: { overlay in
            MapCircle(center: overlay.center, radius: overlay.radius, strokeColor: UIColor(overlay.strokeColor))
        }
    )
    .mapKey(1)
    #if os(macOS)
    .overlay(alignment: .topLeading) {
        MapZoomControl(key: 1)
    }
    #endif
}

Does that work for you? 😊

@ahartman
Copy link
Author

ahartman commented Feb 9, 2023

Paul,

Yes, that works. I tried the same thing but got weird errors. By comparing yours with mine, I found one single stupid typo!

As for the MapZoomControl, I develop for Mac Catalyst, and I presume Catalyst does not qualify as MacOS. That is a pity but I can zoom with double-click and alt-double-click.

Thanks for now and I follow you for updates in your package, good work.

I would be very interested in a solution for clustering annotations; the examples that I find, are old and I cannot came these work.

Regards, André Hartman

@pauljohanneskraft
Copy link
Owner

pauljohanneskraft commented Feb 9, 2023

There is unfortunately no such functionality from MapKit, you can however show the current zoom status by setting the informationVisbility to include MapInformationVisbility.scale - you can do this for example like this:

var body: some View {
    Map(
        coordinateRegion: $region,
        informationVisibility: .default.union(.scale), // You can also try other values such as `.all` to show all kinds of information
        annotationItems: model.mapPins,
        annotationContent: { mapPin in
            MapPin(coordinate: mapPin.coordinate, tint: mapPin.color)
        },
        overlayItems: overlays,
        overlayContent: { overlay in
            MapCircle(center: overlay.center, radius: overlay.radius, strokeColor: UIColor(overlay.strokeColor))
        }
    )
    .mapKey(1)
    #if os(macOS)
    .overlay(alignment: .topLeading) {
        MapZoomControl(key: 1)
    }
    #endif
}

Edit: Ah, I just noticed, that there should actually be support for macCatalyst - I will have a look into this - thank you for bringing it up!

@ahartman
Copy link
Author

ahartman commented Feb 9, 2023

Paul,

According to Apple documentation, there is something like Annotation Declustering, but it very dense for me.
Look at:
https://developer.apple.com/documentation/mapkit/mkannotationview/decluttering_a_map_with_mapkit_annotation_clustering

There are examples on Internet as well, but they are old and I can't transform these to SwiftUI.
Regards, André Hartman

@pauljohanneskraft
Copy link
Owner

#32 should make MapZoomControl and MapPitchControl available for macCatalyst - have you found anything else that doesn't seem to be available for macCatalyst but actually should be?

@pauljohanneskraft
Copy link
Owner

pauljohanneskraft commented Feb 9, 2023

Yes, annotation clustering should be available, I agree, I just haven't found the time nor great ideas to implement it yet. If you happen to have an idea or suggestion, feel free to open another issue or pull request 😊

@pauljohanneskraft
Copy link
Owner

According to Apple documentation, there is something like Annotation Declustering, but it very dense for me. Look at: https://developer.apple.com/documentation/mapkit/mkannotationview/decluttering_a_map_with_mapkit_annotation_clustering

Thank you for that link - unfortunately, I cannot implement this right now, but I will try to find some time in the coming weeks.

@ahartman
Copy link
Author

ahartman commented Feb 10, 2023

Paul,

Please find my code below as as a screen image of the result.
My wife has a psychology practice and the map shows where patients come from in distance from the practice.

import Map
import MapKit
import SwiftUI

struct PatientsPinsView: View {
    @EnvironmentObject var model: MainModel
    var body: some View {
        ZStack {
            doMapView()
            doLegend()
        }
        .navigationBarTitleDisplayMode(.inline)
        .statusBar(hidden: true)
    }
}

struct doMapView: View {
    @EnvironmentObject var model: MainModel
    static var home = CLLocationCoordinate2D(latitude: 51.35988, longitude: 4.48369)
    @State var region = MKCoordinateRegion(
        center: home,
        latitudinalMeters: 40000.0, longitudinalMeters: 40000.0
    )

    var body: some View {
        VStack {
            Map(
                coordinateRegion: $region,
                annotationItems: model.mapPins,
                annotationContent: { element in
                    MapPin(coordinate: element.coordinate, tint: element.color)
                },
                overlayItems: getCircles(),
                overlayContent: { element in
                    MapCircle(
                        center: element.center,
                        radius: element.radius,
                        lineWidth: 3,
                        strokeColor: UIColor(element.strokeColor)
                    )
                }
            )
        }
    }

    func getCircles() -> [MapOverlayCircle] {
        return [
            MapOverlayCircle(center: doMapView.home, radius: 5000.0, strokeColor: .blue),
            MapOverlayCircle(center: doMapView.home, radius: 10000.0, strokeColor: .green),
            MapOverlayCircle(center: doMapView.home, radius: 15000.0, strokeColor: .red)
        ]
    }
}

struct doLegend: View {
    @EnvironmentObject var model: MainModel
    let columns = [GridItem(.fixed(80)), GridItem(.fixed(80)), GridItem(.fixed(80))]
    var body: some View {
        VStack {
            HStack {
                Spacer()
                LazyVGrid(columns: columns) {
                    Text("Afstand")
                        .underline()
                    Text("Aantal")
                        .underline()
                    Text("Procent")
                        .underline()
                    ForEach(model.mapLegend, id: \.self) { legend in
                        Text(legend.distance)
                        Text(legend.count)
                        Text(legend.percentage)
                    }
                }
                .border(.black)
                .background(Color.white.opacity(0.7))
                .padding([.trailing], 50)
                .padding([.top], 20)
                .frame(maxWidth: 240)
            }
            Spacer()
        }
    }
}

struct MapOverlayCircle: Identifiable {
    let id = UUID()
    let center: CLLocationCoordinate2D
    let radius: CLLocationDistance
    let strokeColor: Color
}

struct PatientsPinsView_Previews: PreviewProvider {
    static var previews: some View {
        PatientsPinsView()
    }
}

Schermafbeelding 2023-02-10 om 11 56 53

I also have an earlier version the the map window with MapKit and UIViewRepresentable.
In that window I add the markers not in a closure but in a single statement as an array. It is however the next statement that is important; that statement scales the map automatically so that all makers are displayed.

view.addAnnotations(model.mapMarkers)
view.showAnnotations(view.annotations, animated: true)

The image below shows the result and you can observe a marker in 'Arendonk' on the right hand side that is not in the previous image because that has a fixed scale.

Schermafbeelding 2023-02-10 om 11 57 19

Is there a way to do that automatic scaling in your package as well?

Regards, André Hartman

@pauljohanneskraft
Copy link
Owner

pauljohanneskraft commented Feb 10, 2023

Sure, okay, I have a couple (small) remarks:

  1. Please make sure to have the circles wrapped in @State - otherwise the overlays will be regenerated all the times, since the uuids will change.

  2. There is no such functionality to call this show-method - but you could write a method to center the map accordingly.

Let me give you a helper method that might be of help:

extension MKCoordinateRegion {
    convenience init(containing coordinates: [CLLocationCoordinate2D], multiplyingSpanBy spanFactor: Double = 1) {
        guard let minLatitude = coordinates.map(\.latitude).min(),
                   let maxLatitude = coordinates.map(\.latitude).max(),
                   let minLongitude = coordinates.map(\.longitude).min(),
                   let maxLongitude = coordinates.map(\.longitude).max() else {
            self.init()
            return
        }
        let latitudeDelta = (maxLatitude - minLatitude) * spanFactor // The spanFactor allows you to show a bit more than only the coordinates, e.g. put in `1.5` to see a bit more map than you would actually need to fit the coordinates
        let longitudeDelta = (maxLongitude - minLongitude) * spanFactor
        self.init(
            center: CLLocationCoordinate2D(
                latitude: minLatitude + (latitudeDelta / 2),
                longitude: minLongitude + (longitudeDelta / 2)
            ),
            span: MKCoordinateSpan(
                latitudeDelta: latitudeDelta,
                longitudeDelta: longitudeDelta
            )
        )
    }
}

Warning: This will only work, if the coordinates are not close to the Pacific (which I assume is the case here). An issue would arise, if you had one coordinate in California and one in Japan for example, since the average of their longitudes would be somewhere in Europe, which makes no sense. You would need to use trigonometric functions then to compute the average, which I skipped here.

You can then set your region in an onAppear / task modifier, like this:

@State private var region = MKCoordinateRegion()

var body: some View {
    Map(...)
        .task { region = MKCoordinateRegion(containing: model.mapPins.map(\.coordinate), multiplyingSpanBy: 1.5) }
}

If you need to have this rescaling on every change of the mapPins property, you can use the onChange view modifier instead. If you wrap that assignment of region in a withAnimation, you should also get an animation.

@ahartman
Copy link
Author

ahartman commented Feb 11, 2023

Paul,

Thank you for the Helper, that works.
I moved the helper code to my logic and added a centerPin to check correct working
However, I see 2 weird things in the map.

  1. I use 4 objects, mapPins for the window with Map, mapMarkers for a window with MapKit, mapRegion and mapLegend. You would think both windows would draw the same mapRegion the same but that is not true. it looks like the MapKit window applies padding outside the MKCoordinateregion.
  2. Setting the spanFactor above 1.0 did not work correctly, it shifts the map to the left. I corrected your calculation and it is now OK.

Your calculation:

let latitudeDelta = (maxLatitude - minLatitude) * spanFactor
        let longitudeDelta = (maxLongitude - minLongitude) * spanFactor
        self.init(
            center: CLLocationCoordinate2D(
                latitude: minLatitude + (latitudeDelta / 2),
                longitude: minLongitude + (longitudeDelta / 2)
            ),
            span: MKCoordinateSpan(
                latitudeDelta: latitudeDelta,
                longitudeDelta: longitudeDelta
            )
        )

My correction:

let spanFactor = 1.25
        let latitudeDelta = (maxLatitude - minLatitude) * spanFactor
        let longitudeDelta = (maxLongitude - minLongitude) * spanFactor
        let localRegion = MKCoordinateRegion(
            center: CLLocationCoordinate2D(
                latitude: minLatitude + (maxLatitude - minLatitude)/2.0,
                longitude: minLongitude + (maxLongitude - minLongitude)/2.0
            ),
            span: MKCoordinateSpan(
                latitudeDelta: latitudeDelta,
                longitudeDelta: longitudeDelta
            )
        )

Image 1 is the Map window with spanFactor at 1.0.
Image 2 is the MapKit window with spanFactor at 1.0. please observe that it shows a little more.
Image 3 is the Map window with spanFactor at 1.25. The extra room is completely on the right hand side of the map.
Image 4 is the Map window with spanFactor at 1.25 and the corrected calculation.

Image 1
Schermafbeelding 2023-02-12 om 07 40 37

Image 2
Schermafbeelding 2023-02-12 om 07 40 44

Image 3
Schermafbeelding 2023-02-12 om 07 43 10

Image4
Schermafbeelding 2023-02-12 om 07 51 47

Regards, André Hartman

@ahartman
Copy link
Author

ahartman commented Feb 12, 2023

Paul,

Please find my code. below:
Map window:

import Map
import MapKit
import SwiftUI

struct PatientsPinsView: View {
    var body: some View {
        ZStack {
            doMapView()
            doLegend()
        }
        .navigationBarTitleDisplayMode(.inline)
        .statusBar(hidden: true)
    }
}

struct doMapView: View {
    @EnvironmentObject var model: MainModel
    let home = CLLocationCoordinate2D(latitude: 51.35988, longitude: 4.48369)

    var body: some View {
        VStack {
            Map(
                coordinateRegion: $model.mapRegion,
                annotationItems: model.mapPins,
                annotationContent: { element in
                    MapPin(coordinate: element.coordinate, tint: element.color)
                },
                overlayItems: getCircles(),
                overlayContent: { element in
                    MapCircle(
                        center: element.center,
                        radius: element.radius,
                        lineWidth: 3,
                        strokeColor: UIColor(element.strokeColor)
                    )
                }
            )
        }
    }

    func getCircles() -> [MapOverlayCircle] {
        return [
            MapOverlayCircle(center: model.home, radius: 5000.0, strokeColor: .blue),
            MapOverlayCircle(center: model.home, radius: 10000.0, strokeColor: .green),
            MapOverlayCircle(center: model.home, radius: 15000.0, strokeColor: .red)
        ]
    }
}

struct doLegend: View {
    @EnvironmentObject var model: MainModel
    let columns = [GridItem(.fixed(80)), GridItem(.fixed(80)), GridItem(.fixed(80))]
    var body: some View {
        VStack {
            HStack {
                Spacer()
                LazyVGrid(columns: columns) {
                    Text("Afstand")
                        .underline()
                    Text("Aantal")
                        .underline()
                    Text("Procent")
                        .underline()
                    ForEach(model.mapLegend, id: \.self) { legend in
                        Text(legend.distance)
                        Text(legend.count)
                        Text(legend.percentage)
                    }
                }
                .border(.black)
                .background(Color.white.opacity(0.7))
                .padding([.trailing], 50)
                .padding([.top], 20)
                .frame(maxWidth: 240)
            }
            Spacer()
        }
    }
}

struct MapOverlayCircle: Identifiable {
    let id = UUID()
    let center: CLLocationCoordinate2D
    let radius: CLLocationDistance
    let strokeColor: Color
}

struct PatientsPinsView_Previews: PreviewProvider {
    static var previews: some View {
        PatientsPinsView()
    }
}

MapKit window

import MapKit
import SwiftUI

struct PatientsMarkersView: View {
    var body: some View {
        ZStack {
            provideMapView()
        }
        .navigationBarTitleDisplayMode(.inline)
        .statusBar(hidden: true)
    }
}

struct provideMapView: UIViewRepresentable {
    @EnvironmentObject var model: MainModel

    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView(frame: UIScreen.main.bounds)
        mapView.delegate = context.coordinator
        mapView.setRegion(model.mapRegion, animated: true)
        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
         for (index, count) in model.mapLegend.enumerated() {
            if index < model.mapLegend.endIndex - 1 {
                let circle = MKCircle(center: model.home, radius: count.roundedDistance)
                view.addOverlay(circle)
            }
        }
        view.addAnnotations(model.mapMarkers)
        //view.showAnnotations(view.annotations, animated: true)
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: provideMapView
        init(_ parent: provideMapView) {
            self.parent = parent
        }

        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
 
            let color = (annotation as! CustomMarker).color
            view.pinTintColor = UIColor(color)
            return view
        }

        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            if let circleOverlay = overlay as? MKCircle {
                let renderer = MKCircleRenderer(overlay: circleOverlay)
                renderer.strokeColor = circleColor(radius: circleOverlay.radius)
                renderer.lineWidth = 3
                return renderer
            }
            return MKOverlayRenderer()
        }
    }
}

public func circleColor(radius: Double) -> UIColor {
    switch radius {
    case circles.circle1.rawValue:
        return UIColor(circleColor1)
    case circles.circle2.rawValue:
        return UIColor(circleColor2)
    case circles.circle3.rawValue:
        return UIColor(circleColor3)
    case circles.circle4.rawValue:
        return UIColor(circleColor4)
    default:
        return UIColor(.black)
    }
}

class CustomMarker: NSObject, MKAnnotation {
    let coordinate: CLLocationCoordinate2D
    let color: Color

    init(coordinate: CLLocationCoordinate2D, color: Color) {
        self.coordinate = coordinate
        self.color = color
        super.init()
    }
}

MapModel logic

class MapModel {
    func doMapView(patients: [String]) -> ([PatientPin], [LegendLine], MKCoordinateRegion, [CustomMarker]) {
        let geoPatients: [CNContact] = ContactsModel().geoPatients(patients: patients)

        var localPins = getPins(patients: geoPatients)
        let localLegend = getLegend(pins: localPins)
        let (localRegion, centerPin) = getRegion(pins: localPins)
        var localMarkers = getMarkers(pins: localPins)

        localPins.append(centerPin)
        localMarkers.append(CustomMarker(coordinate: centerPin.coordinate, color: centerPin.color))

        return (localPins, localLegend, localRegion, localMarkers)
    }

    func getPins(patients: [CNContact]) -> [PatientPin] {
        let rodeweg = PatientPin(coordinate: .init(latitude: 51.35988, longitude: 4.48369), radius: 0.0, color: .yellow)
        let rodewegDistance = CLLocation(latitude: 51.35988, longitude: 4.48369)

        var localPins = [PatientPin]()
        for patient in patients {
            let coordinates = patient.previousFamilyName.components(separatedBy: ":")
            let location = CLLocation(latitude: Double(coordinates[0])!, longitude: Double(coordinates[1])!)
            let distance = location.distance(from: rodewegDistance)

            var roundedDistance = 0.0
            circles.allCases.reversed().forEach {
                if distance < $0.rawValue {
                    roundedDistance = $0.rawValue
                }
            }
            let pinColor = Color(circleColor(radius: roundedDistance))
            localPins.append(PatientPin(coordinate: .init(latitude: Double(coordinates[0])!, longitude: Double(coordinates[1])!), radius: roundedDistance,  color: pinColor))
        }
        localPins.append(rodeweg)
        return localPins
    }

    func getMarkers(pins: [PatientPin]) -> [CustomMarker] {
        var localMarkers = [CustomMarker]()
        for pin in pins {
            localMarkers.append(CustomMarker(coordinate: pin.coordinate, color: pin.color))
        }
        return localMarkers
    }

    func getLegend(pins: [PatientPin]) -> [LegendLine]{
        var localLegend = [LegendLine]()
        var oldDistance = 0.0
        var counterTotal = 0
        for (index, circle) in circles.allCases.enumerated() {
            let distance = circle.rawValue
            let counter = pins.filter { $0.radius == distance }.count
            counterTotal += counter
            let color = circleColor(radius: distance)
            let textDistance = (index == circles.allCases.count - 1) ? "> \(Int(oldDistance/1000)) km" : "< \(Int(distance/1000)) km"
            localLegend.append(LegendLine(roundedDistance: distance, distance: textDistance, count: String(counter), percentage: "0%", color: Color(color)))
            oldDistance = distance
        }

        var percent = 0.0
        for (index, _) in localLegend.enumerated() {
            percent += Double(localLegend[index].count)!/Double(counterTotal)
            let roundedPercentage = Int(round(percent * 20)/20 * 100)
            localLegend[index].percentage = String("\(roundedPercentage)%")
        }
        return localLegend
    }

    func getRegion(pins: [PatientPin]) -> (MKCoordinateRegion, PatientPin) {
        let coordinates = pins.map(\.coordinate)
        let minLatitude = coordinates.map(\.latitude).min()!
        let maxLatitude = coordinates.map(\.latitude).max()!
        let minLongitude = coordinates.map(\.longitude).min()!
        let maxLongitude = coordinates.map(\.longitude).max()!

        let spanFactor = 1.25
        let latitudeDelta = (maxLatitude - minLatitude) * spanFactor
        let longitudeDelta = (maxLongitude - minLongitude) * spanFactor
        let localRegion = MKCoordinateRegion(
            center: CLLocationCoordinate2D(
                latitude: minLatitude + (maxLatitude - minLatitude)/2.0,
                longitude: minLongitude + (maxLongitude - minLongitude)/2.0
            ),
            span: MKCoordinateSpan(
                latitudeDelta: latitudeDelta,
                longitudeDelta: longitudeDelta
            )
        )
        let centerPin = PatientPin(coordinate: localRegion.center, radius: 0.0, color: .purple)
        return (localRegion, centerPin)
    }

@pauljohanneskraft
Copy link
Owner

Thank you for the correction of the code snippet - yes, there seems to have been an issue, but you seem to have found the cause correctly.

On the other note, you seem to be setting the mapRegion at two different points in time. While the implementation using this library sets the region once the view is actually in the view hierarchy, your custom implementation sets it before the view enters the view hierarchy with a frame of UIScreen.main.bounds. This means, there is probably some map rescaling involved once the view is no longer that size but is rather resized to the safeArea instead of the full bounds (if there are navigationbars etc involved, it will be even smaller).

In general, I can see that a lot of data is generated while calculating the body properties of the view rather than beforehand. Especially in the case of getCircles, but also in the case of your updateUIView, this will insert/remove annotations and overlays on any change of data, which is often unnecessary and may result in lags and sometimes even crash the app (If this happens too much - this is an issue of MKMapView and not this library).

@pauljohanneskraft
Copy link
Owner

Since your original question and hopefully all further questions are solved, I will close this thread. If you happen to have follow-up questions, feel free to reopen / add comments and for unrelated questions feel free to open new issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants