Reputation: 2562
I want to add a moving circle on MKMapView it will remain in the centre of the map and start moving when the user moves the map and zoom in and zoom out when the user zooms in/out the map I am able to achieve this by using the centre coordinate of the map but when I move the app it is flickering as I am removing and adding the MKCircle overlay when the coordinate changes
What I want to achieve:- A moving circle/Annotation on top of the mapview which moves/zoom along with mapview
Here is the code
Adding pan gesture and circle overlay here
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
mapView.isUserInteractionEnabled = true
mapView.showsUserLocation = true
mapView.showsCompass = false
let panGesture = UIPanGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.addPinBasedOnGesture(_:)))
panGesture.delegate = context.coordinator
mapView.addGestureRecognizer(panGesture)
return mapView
}
Adding and removing method which updated the moving circle coordinate to
func updateMovingCircle(regionToUpdate: MKCoordinateRegion, view: MKMapView) {
DispatchQueue.main.async {
addMovingCircle(to: view, movingLocation: CLLocationCoordinate2D(latitude: regionToUpdate.center.latitude, longitude: regionToUpdate.center.longitude), radius: radius)
}
}
func to add and remove circle
func addMovingCircle(to view: MKMapView, movingLocation: CLLocationCoordinate2D, radius: Int) {
for overlay in overlays where overlay.title == "1" {
view.removeOverlay(overlay)
}
let radius: Double = Double(radius) * 1.5
let movingCircle = MKCircle(center: movingLocation, radius: radius * 1000)
movingCircle.title = "1"
view.addOverlay(movingCircle)
}
gesture recognizer delegate method on coordinator
@objc func addPinBasedOnGesture(_ gestureRecognizer: UIGestureRecognizer) {
if let mapView = gestureRecognizer.view as? MKMapView {
mapViewController.updateMovingCircle(regionToUpdate: mapView.region, view: mapView)
}
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Overlay renderer
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let circleOverlay = overlay as? MKCircle {
let circleRenderer = MKCircleRenderer(overlay: circleOverlay)
circleRenderer.strokeColor = UIColor(Color.SG.purple)
circleRenderer.lineWidth = 2
circleRenderer.lineDashPattern = [2, 5]
return circleRenderer
}
return MKOverlayRenderer()
}
This code is working but the experience is not smooth and circle is flickering
Upvotes: 0
Views: 112
Reputation: 8866
I would like to go with a different approach. You can add the entire MapView inside a ZStack and a MKCircle
above the map. Then, no matter how you interact with the map, MKCircle will stay at the center.
var body: some View {
@State private var zoomFactor: Double = 10
ZStack {
MapViewSUI(zoomFactor: $zoomFactor)
Image(systemName: "mappin.circle")
.resizable()
.frame(width: 64 * (zoomFactor / 10), height: 64 * (zoomFactor / 10))
.foregroundColor(.red)
}
}
Updated: Here is the idea for zoom action
struct MapViewSUI: UIViewRepresentable {
@Binding var zoomFactor: Double
...
class Coordinator: NSObject, MKMapViewDelegate {
var map: MapViewSUI
...
//This is formula to calculate zoom level and it is not my code
func getZoomLevel(from mapView: MKMapView) -> Double {
log2(360 * ((mapView.frame.size.width/256.0) / mapView.region.span.longitudeDelta));
}
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
map.zoomFactor = getZoomLevel(from: mapView)
}
}
}
You may want to change the view size or add animation if needed. Output looks like this:
Upvotes: 0