Been trying to figure this out for hours. Trying to do something simple: draw a circle overlay on mapview. BLEvent conforms to the MKMapOverLay protocol and has the information needed (radius, coordinates) Yes my view controller has been set as the map view delegate.
In my viewDidLoad..
for (BLEvent *event in self.model.events) {
[self.mapView addOverlay:event];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay{
if ([overlay isKindOfClass:[BLEvent class]]){
BLEvent *event = (BLEvent *)overlay;
MKCircle *circle = [MKCircle circleWithCenterCoordinate:event.coordinate radius:event.radius];
MKCircleRenderer *circleR = [[MKCircleRenderer alloc] initWithCircle:circle];
circleR.fillColor = [UIColor blueColor];
return circleR;
} else{
return nil;
}
}
I checked with this, and the renderer was in fact called> I know the coordinates are valid because I had annotations that showed in the map.
- (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray *)renderers{
NSLog(#"%#", ((MKCircleRenderer *)[renderers objectAtIndex:0]).fillColor);
}
The circle is not drawn because the MKCircle you create is not added as overlay to the map.
In order to make this work your BLEvent class needs to be a MKCircle subclass.
Then try this:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay{
if ([overlay isKindOfClass:[BLEvent class]]){
BLEvent *event = (BLEvent *)overlay;
MKCircleRenderer *circleR = [[MKCircleRenderer alloc] initWithCircle:event];
circleR.fillColor = [UIColor blueColor];
return circleR;
}
return nil;
}
A working version for iOS 8.0 and SWIFT
import Foundation
import MapKit
class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate{
var locationManager: CLLocationManager = CLLocationManager()
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// We use a predefined location
var location = CLLocation(latitude: 46.7667 as CLLocationDegrees, longitude: 23.58 as CLLocationDegrees)
addRadiusCircle(location)
}
func addRadiusCircle(location: CLLocation){
self.mapView.delegate = self
var circle = MKCircle(centerCoordinate: location.coordinate, radius: 10000 as CLLocationDistance)
self.mapView.addOverlay(circle)
}
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKCircle {
var circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = UIColor.redColor()
circle.fillColor = UIColor(red: 255, green: 0, blue: 0, alpha: 0.1)
circle.lineWidth = 1
return circle
} else {
return nil
}
}
}
Related
I would like to place an image in a map overlay which will scale along with the map. Using the code below, the image appears in the map view but it is stretched to fit the view. How can I keep the original aspect ratio of the image inside the overlay?
MapOverlay.swift
import UIKit
import MapKit
class MapOverlay: NSObject, MKOverlay {
var coordinate: CLLocationCoordinate2D
var boundingMapRect: MKMapRect
init(coord: CLLocationCoordinate2D, rect: MKMapRect) {
self.coordinate = coord
self.boundingMapRect = rect
}
}
MapOverlayView.swift
import UIKit
import MapKit
class MapOverlayView: MKOverlayRenderer {
var overlayImage: UIImage
init(overlay: MKOverlay, overlayImage:UIImage) {
self.overlayImage = overlayImage
super.init(overlay: overlay)
}
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
let mapImage = overlayImage.cgImage
let mapRect = rect(for: overlay.boundingMapRect)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0.0, y: -mapRect.size.height)
context.draw(mapImage!, in: mapRect)
}
}
ViewController.swift
import UIKit
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapview: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapview.delegate = self
let location = CLLocationCoordinate2D(latitude: 47.6062, longitude: -122.3320)
let span = MKCoordinateSpanMake(2.0, 2.0)
let region = MKCoordinateRegion(center: location, span: span)
mapview.setRegion(region, animated: true)
let rec = mapview.visibleMapRect
let overlay = MapOverlay(coord: location, rect: rec)
mapview.add(overlay)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MapOverlay {
let logo = UIImage(named: "swift")
let overlayView = MapOverlayView(overlay: overlay, overlayImage: logo)
return overlayView
} else {
return MKPolylineRenderer()
}
}
}
In your ViewController.swift, in viewDidLoad(),
let rec = mapview.visibleMapRect
Change this, rec should be exactly same size of the image used.
let location = //Give your location here in CLLocationCoordinate2D
//1. Show direction Using Overlays
let span = MKCoordinateSpanMake(1.0, 1.0)
let region = MKCoordinateRegion(center: location, span: span)
let mapRect: MKMapRect = helperClass.MKMapRectForCoordinateRegion(region: region)
let overlay = MapOverlay(identifier: title, coord: location, rect: mapRect)
mapView.add(overlay)
The function to get MKMapRect
func MKMapRectForCoordinateRegion(region:MKCoordinateRegion) -> MKMapRect {
let topLeft = CLLocationCoordinate2D(latitude: region.center.latitude + (region.span.latitudeDelta/2), longitude: region.center.longitude - (region.span.longitudeDelta/2))
let bottomRight = CLLocationCoordinate2D(latitude: region.center.latitude - (region.span.latitudeDelta/2), longitude: region.center.longitude + (region.span.longitudeDelta/2))
let a = MKMapPointForCoordinate(topLeft)
let b = MKMapPointForCoordinate(bottomRight)
return MKMapRect(origin: MKMapPoint(x:min(a.x,b.x), y:min(a.y,b.y)), size: MKMapSize(width: abs(a.x-b.x), height: abs(a.y-b.y)))
}
The sample project can be found in this link
I have very simple View Controller to demonstrate this strange rendering behavior of MKPolyline. Nothing special just normal api calls.
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
map.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let p1 = CLLocationCoordinate2D(latitude: 51, longitude: 13)
var coords = [
p1,
CLLocationCoordinate2D(latitude: 51.1, longitude: 13),
CLLocationCoordinate2D(latitude: 51.2, longitude: 13),
CLLocationCoordinate2D(latitude: 51.3, longitude: 13)
]
let polyline = MKPolyline(coordinates: &coords, count: coords.count)
map.addOverlays([polyline], level: .aboveRoads)
let cam = MKMapCamera(lookingAtCenter: p1, fromDistance: 1000, pitch: 45, heading: 0)
map.setCamera(cam, animated: true)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let r = MKPolylineRenderer(overlay: overlay)
r.strokeColor = UIColor.blue
return r
}
}
The rendering of the polyline is very strange. During zooming and panning You can see some artifacts.
Take a look at pictures below:
Initial Screen
After some panning
After zooming out and zooming in again
How to fix this? I was trying to implement my own renderer but its the same situation. Like overaly is cached and it's not redrawing on time. I'm working on iOS 10, iPhone 6, Simulator from iOS SDK 10 xCode 8.
Swift 3 solution :
Create a subclass of MKPolylineRenderer
class CustomPolyline: MKPolylineRenderer {
override func applyStrokeProperties(to context: CGContext, atZoomScale zoomScale: MKZoomScale) {
super.applyStrokeProperties(to: context, atZoomScale: zoomScale)
UIGraphicsPushContext(context)
if let ctx = UIGraphicsGetCurrentContext() {
ctx.setLineWidth(self.lineWidth)
}
}
}
Then use it in your rendererFor MapKit delegate :
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = CustomPolyline(overlay: overlay)
renderer.strokeColor = UIColor.red
renderer.lineWidth = 100
return renderer
}
Your polylines won't re-render after zooming thus avoiding the artifacts
I'm trying to add a circle overlay to my map. I followed the apple docs and got this far, but the overlay doesn't appear when I run the app. Here's my code...
#IBOutlet var map: MKMapView!
let location = CLLocationCoordinate2DMake(40.73085, -73.99750)
let regionRadius: CLLocationDistance = 5500
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let diskRenderer: MKCircleRenderer = MKCircleRenderer.init()
diskRenderer.fillColor = UIColor.init(red: 0, green: 192, blue: 295, alpha: 1)
return diskRenderer
}
override func viewDidLoad() {
super.viewDidLoad()
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location, regionRadius, regionRadius)
map.setRegion(coordinateRegion, animated: true)
let diskOverlay: MKCircle = MKCircle.init(centerCoordinate: location, radius: 5000)
map.addOverlay(diskOverlay)
// Do any additional setup after loading the view.
}
I'm teaching myself all this stuff so forgive me if i've made an obvious mistake.
See the accepted answer to this question:
How to create MKCircle in Swift?
Steps are:
Ensure this controller implements MKMapViewDelegate:
class YourViewController: UIViewController, MKMapViewDelegate
In viewDidLoad set the delegate on map to self:
override func viewDidLoad() {
super.viewDidLoad()
map.delegate = self;`
Add the following function to render the overlay (change the color, etc):
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
if let overlay = overlay as? MKCircle {
let circleRenderer = MKCircleRenderer(circle: overlay)
circleRenderer.fillColor = UIColor.blueColor()
return circleRenderer
}
else {
return MKOverlayRenderer(overlay: overlay)
}
}
The radius of your circle is too big as it is currently set and the whole map will be blue. Set it to something small, like 1000, so you can see the circle.
objc in a class that is set as a mkmapviewdelegate:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKPolygon class]]) {
MKPolygon *polygon = (MKPolygon *)overlay;
MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:polygon];
renderer.strokeColor = [UIColor blueColor];
renderer.lineWidth = 5.;
return renderer;
}
return [[MKOverlayRenderer alloc] initWithOverlay:overlay];
}
Could someone point me in the right direction for adding a radius to my point. Its pretty standard code, I've tried adding a MKcircle but that hasn't anything to my map.
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var maps: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
var latitude:CLLocationDegrees = 54.53
var longitude:CLLocationDegrees = -6.055
var latDelta:CLLocationDegrees = 0.015 //difference of lats from one side of screen to another
var longDelta:CLLocationDegrees = 0.015 //difference of lats from one side of screen to another
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude,longitude)
var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
var lenght:CLLocationDistance = 2
var point = MKPointAnnotation()
self.maps.delegate = self
point.title = "Home"
point.subtitle = "time for home"
point.coordinate = location
maps.addAnnotation(point)
maps.selectAnnotation(point, animated: true)
var cir:MKCircle = MKCircle(centerCoordinate: location, radius: length) //added this but nothing is displayed on map
maps.addOverlay(cir)
maps.setRegion(MKCoordinateRegionMake(point.coordinate, MKCoordinateSpanMake(latDelta,longDelta)), animated: true)
// Do any additional setup after loading the view, typically from a nib.
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if (annotation.isKindOfClass(MKUserLocation)){
return nil
}
var myPin = mapView.dequeueReusableAnnotationViewWithIdentifier("MyIdentifier") as? MKPinAnnotationView
if myPin != nil {
return myPin
}
myPin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "MyIdentifier")
myPin?.pinColor = .Green
return myPin
}
Implement rendererForOverlay delegate:
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
var overlayRenderer : MKCircleRenderer = MKCircleRenderer(overlay: overlay);
overlayRenderer.lineWidth = 1.0
overlayRenderer.strokeColor = UIColor.redColor()
return overlayRenderer
}
I use the following delegate method to draw route on Map. It works fine most of the time except in one route that has large coordinates array. Sometimes it can cast the MKOverlay to MKPolyline and sometimes it cannot for the same route. Any idea why this is happening?
public override MKOverlayView GetViewForOverlay (MKMapView mapView, IMKOverlay overlay)
{
if (overlay is MKPolyline) {
MKPolyline polyline = overlay as MKPolyline;
MKPolylineView polylineView = new MKPolylineView (polyline);
polylineView.StrokeColor = UIColor.Blue;
polylineView.LineWidth = 5.0f;
return polylineView;
}
return null;
}
This code for Objective C and Translate it. Draw route polyLine on map as below code,
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blueColor()
polylineRenderer.lineWidth = 5
return polylineRenderer
}
return nil
}
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
var type = overlayWrapper.GetType();
var overlay = overlayWrapper as IMKOverlay;
if (overlay is MKPolyline || type == typeof(MKPolyline))
{
if (polylineRenderer == null)
{
polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline);
polylineRenderer.FillColor = UIColor.Blue;
polylineRenderer.StrokeColor = UIColor.Red;
polylineRenderer.LineWidth = 3;
polylineRenderer.Alpha = 0.4f;
}
return polylineRenderer;
}
}
I have the same problem, but this code works on the Simulator but not on a device.