Draw polygon on MapView using Swift - swift

I have a MKMapView, and a set of coordinates I want to connect with a polygon to draw a rectangle. Below is my code that I have started with.
import UIKit
import MapKit
class mapViewViewController: UIViewController {
#IBOutlet var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
map?.delegate=self
let startingPoint1 = ...
let startingPoint2 = ...
let endingPoint1 = ...
let endingPoint2 = ...
var coordinateInput:[CLLocationCoordinate2D]=[startingPoint1,startingPoint2,endingPoint1,endingPoint2]
let line = MKPolygon(coordinates:&coordinateInput, count:4)
map.addOverlay(line)
}
extension mapViewViewController: MKMapViewDelegate{
func map(_ map: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer{
if overlay is MKPolygon{
let renderer = MKPolygonRenderer(polygon: overlay as! MKPolygon)
renderer.fillColor=UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor=UIColor.orange
renderer.lineWidth=4
return renderer
}
return MKOverlayRenderer()
}
}
The issue I'm running into is that when I launch the map view nothing is displayed on top of the map as expected. I have confirmed all of my coordinates are valid and what I intended them to be, but my current setup is not drawing on the map as I expect. What is the correct way to accomplish this?

Your signature for rendererFor is incorrect. Add a breakpoint or log statement in your method and you will see it is not getting called.
The correct signature is:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { ... }

Related

Google Maps API didDrag, didBeginDragging, didEndDragging functions do not work

I have set up a single marker on the map view and I am using GMSMapViewDelegate and implementing the following functions:
mapView:didBeginDraggingMarker:
mapView:didEndDraggingMarker:
mapView:didDragMarker:
I have tried reinstalling the pods but nothing I do seems to work. I have been stuck on this problem for the past 5 days and still no luck.
import GoogleMaps
class MapViewController: UIViewController, GMSMapViewDelegate {
let marker = GMSMarker()
var mapView = GMSMapView()
override func viewDidLoad() {
super.viewDidLoad()
// This function fetches data from the server and places marker
makeRequest()
self.marker.isDraggable = true
// Create a map with the location based on user's country
let camera = GMSCameraPosition.camera(withLatitude: defaults.value(forKey: map.lat) as! CLLocationDegrees,
longitude: defaults.value(forKey: map.lon) as! CLLocationDegrees,
zoom: defaults.value(forKey: map.zoom) as! Float)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
}
func mapView(_ mapView: GMSMapView, didBeginDragging marker: GMSMarker) {
print("DidBeginDragging")
}
func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
print("didEndDragging")
}
func mapView(_ mapView: GMSMapView, didDrag marker: GMSMarker) {
print("didDrag")
}
}
I can drag the marker but I cannot seem to run the three delegate functions.
Looks like you missed setting the delegate:
mapView.delegate = self
at the top change the var mapView = GMSMapView() to var mapView: GMSMapView?
The way you have it, you're creating a mapView and then creating a new one in viewDidLoad
Documentation

(SwiftUI) MKOverlays (parsed from GeoJSON) are added, but do not show on screen, am I missing something?

I am attempting to show a UIKit/MapKit view in SwiftUI, following code very similar to this project on Github but implementing the GeoJSON render/overlay code from the Apple Guide for Optimizing Map Views with Filtering and Camera Constraints.
I have an 'EventDataSource' class which (successfully) parses the GeoJSON objects from a local file, in a UIViewRepresentable class, I then load this data, and add the overlays in the updateUIView function.
Placing a breakpoint in the mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) function shows that each polygon has the correct value for _pointCount, and I have added a print() line for the coordinates, which successfully prints out the coordinates of all the objects whenever the view is loaded.
Xcode shows no errors and builds fine, when run, the MapKit view loads, yet no overlays appear on screen.
The exact same mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) function, used in a UIKit ViewController renders the objects perfectly. In SwiftUI, I get successful parsing, yet only an empty map. Is there something I am missing?
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var dataSource: EventDataSource = EventDataSource()
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.delegate = context.coordinator
return map
}
func updateUIView(_ mapView: MKMapView, context: Context) {
mapView.region = .event
mapView.addOverlays(dataSource.overlays)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, MKMapViewDelegate {
var control: MapView
init(_ control: MapView) {
self.control = control
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let polygon = overlay as? MKPolygon {
let renderer = MKPolygonRenderer(polygon: polygon)
renderer.fillColor = UIColor(named: "OverlayFill")
renderer.strokeColor = UIColor(named: "OverlayStroke")
renderer.lineWidth = 1.0
print("\(polygon.coordinate.longitude), \(polygon.coordinate.latitude)") // succesfully prints the coordinate of each object
return renderer
}
else if let multiPolygon = overlay as? MKMultiPolygon {
let renderer = MKMultiPolygonRenderer(multiPolygon: multiPolygon)
renderer.fillColor = UIColor(named: "OverlayFill")
renderer.strokeColor = UIColor(named: "OverlayStroke")
renderer.lineWidth = 1.0
print("\(multiPolygon.coordinate.longitude), \(multiPolygon.coordinate.latitude)") // succesfully prints the coordinate of each object
return renderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
}
I have tried commenting out the line
mapView.addOverlays(dataSource.overlays)
When this line is not there, the coordinates do not print to the console, so I am sure that the function is being called.
Any help will be greatly appreciated, thanks!
if you try this
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
var coordinate: CLLocationCoordinate2D
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
}
}
then in your view add
MapView(coordinate: "the coordinates")
check out the
https://developer.apple.com/tutorials/swiftui/creating-and-combining-views

I need to show my location with the normal blue dot and another point with a different marker

I changed the design of a marker in the mapView, but in doing this I changed the marker of my location and it was not what I wanted. I just want to change the marker of MIT, and the one of my location to leave it only with the normal blue marker that appears on all maps.
Here I leave some code that could be failing.
mapView.register(MKMarkerAnnotationView.self,
forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
let mitCoordinate = CLLocationCoordinate2D(latitude: -41.471373559102965, longitude: -72.94215917587279)
let mitAnnotation = SchoolAnnotation(coordinate: mitCoordinate, title: "MIT", subtitle: "mit - USA")
mapView.addAnnotation(mitAnnotation)
mapView.setRegion(mitAnnotation.region, animated: true)
extension BusinessViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let schoolAnnotationView
= mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) as? MKMarkerAnnotationView {
schoolAnnotationView
.animatesWhenAdded = true
schoolAnnotationView
.titleVisibility = .adaptive
schoolAnnotationView
.titleVisibility = .adaptive
return schoolAnnotationView
}
return nil
}
Here's an image:
The problem is that your implementation of
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
does not look to see whether this annotation is your MIT annotation or your user location annotation. You need to start with an if condition and check that. If it's the user location, you return nil to get the default marker.

How to draw "rotating" arrow as annotation?

Ok, my new task is this: inside a tracker app I have a map which I'm trying to annotate with custom heading arrow. I need this arrow to be rotated to a certain angle.
I have all the data (i.e. dergrees and locations) and this method:
headingImageView?.transform = CGAffineTransform(rotationAngle: CGFloat(rotationAngle))
So I tried:
Creating an annotation with
let annotation = UserAnnotation(coordinate: locations.last!.coordinate)
mapView.addAnnotation(annotation)
Conforming to MKMapViewDelegate protocol
extension TrackViewController: MKMapViewDelegate
Implementing methods like :viewFor: and also :rendererFor:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if !overlay.isKind(of: MKPolyline.classForCoder()) {
return MKPolylineRenderer(overlay: overlay)
}
let polyline = overlay as! MKPolyline
let renderer = MKPolylineRenderer(polyline: polyline)
renderer.strokeColor = .blue
renderer.lineWidth = 3
return renderer
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is UserAnnotation {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Online")
annotationView.image = UIImage(named: "arrow")
return annotationView
}
}
And so it kind of works, but I need to rotate an image every time location is updated. How should I do that? Any advice would be appreciated
you can use this code :
but this is important to know you have to put (radian) for rotationAngle
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is UserAnnotation {
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "Online")
annotationView.image = UIImage(named: "arrow")
annotationView.transform = CGAffineTransform(rotationAngle: CGFloat(//what you want with type of radian))
return annotationView
}
}
Well, for now I did that:
Globally declared image of an arrow and an optional annotation
let arrow = #imageLiteral(resourceName: "arrow")
var rotatedArrow: UIImage?
var oldAnnotation: MKPointAnnotation?
Rotated image to a certain angle when I get this angle
rotatedArrow = arrow.imageRotated(by: CGFloat(locations.last!.course))
Passed this rotated image to the delegate's method as
annotationView.image = rotatedArrow
return annotationView
Stored and deleted previous annotation
But it all looks soooo ugly

Swift MapKit - create a polygon

I am trying to create a polygon on my map to show a boundry. But when I run my app nothing appears. I can see the map but not the polygon What's my error? thanks.
import UIKit
import MapKit
import CoreLocation
class MapScreen : UIViewController {
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
mapView.delegate = self as! MKMapViewDelegate
// center the location on the screen
var location = CLLocationCoordinate2DMake(41.308792, -72.928641)
var span = MKCoordinateSpanMake(0.01, 0.01)
var region = MKCoordinateRegionMake(location, span)
mapView.setRegion(region, animated: true)
//calling the method
addBoundry()
}
func addBoundry(){ //creation of a polygon
var points = [CLLocationCoordinate2DMake(41.311674, -72.925506),
CLLocationCoordinate2DMake(41.307308, -72.928694),
CLLocationCoordinate2DMake(41.307108, -72.928324),
CLLocationCoordinate2DMake(41.307892, -72.930285),
CLLocationCoordinate2DMake(41.307892, -72.931223),
CLLocationCoordinate2DMake(41.307227, -72.932494),
CLLocationCoordinate2DMake(41.308452, -72.931663),
CLLocationCoordinate2DMake(41.308730, -72.932773),
CLLocationCoordinate2DMake(41.308496, -72.931614),
CLLocationCoordinate2DMake(41.308496, -72.931614),
CLLocationCoordinate2DMake(41.311288, -72.931630),
CLLocationCoordinate2DMake(41.311659, -72.930945),
CLLocationCoordinate2DMake(41.312893, -72.932153),
CLLocationCoordinate2DMake(41.313433, -72.930542),
CLLocationCoordinate2DMake(41.313324, -72.929963),
CLLocationCoordinate2DMake(41.312758, -72.929027),
CLLocationCoordinate2DMake(41.312373, -72.927167),
CLLocationCoordinate2DMake(41.311674, -72.925506)]
let polygon = MKPolygon(coordinates: &points, count: points.count)
mapView.add(polygon)
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.fillColor = UIColor(red: 0, green: 0.847, blue: 1, alpha: 0.25)
return polygonView
}
return nil
}
}
Your signature for mapView(_:rendererFor:) is incorrect. In Swift 3, it would be:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
...
}
If you formally declare your conformance to protocols like MKMapViewDelegate, it will often warn you about these things. For example, best practice would be do so in a class extension rather than in the class definition itself:
extension MapScreen: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
...
}
}
By the way, if you do that, not only do you get warnings about protocol conformance, but you also don't need to cast it when setting the delegate:
mapView.delegate = self