My code correctly gets an image and provides the bounds for the mapView. I want to put the image within the bounds but I am not sure how. I am able to fill it with a color but I need help making it an image.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolygonRenderer(overlay: overlay)
//renderer.fillColor = UIColor.red -> works
return renderer
}
How can I fill it with this image that I downloaded?https://radar.weather.gov/ridge/Conus/RadarImg/latest_radaronly.gif
Using this subclass of MKOverlayRenderer you will be able to render UIImage overlay on the actual MKPolygon
class CustomOverlayRenderer: MKOverlayRenderer {
private let overlayImage: UIImage?
init(overlay: MKOverlay, image: UIImage? = nil) {
self.overlayImage = image
super.init(overlay: overlay)
}
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
guard let cgImage = self.overlayImage?.cgImage else { return }
let rect = self.rect(for: overlay.boundingMapRect)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0, y: -rect.height)
context.draw(cgImage, in: rect)
}
}
Just change your delegate method to this use CustomOverlayRenderer, like so:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
return CustomOverlayRenderer(overlay: overlay, image: UIImage(named: "latest_radaronly.gif")!)
}
Related
I'm trying to create simple custom map annotations on Mapbox iOS (5.5.0) by implementing MGLMapViewDelegate protocol. Annotations are actually added to the map, but they are invisible. I can predict its coordinate and open its callout, so, I know it's there.
I have even tried copying the exact codes given on Mapbox examples to get the same result. Can it be a lack of some framework elements?
My code:
class MainViewController: UIViewController, MGLMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Set the map’s size, style, center coordinate, zoom level, and tint color.
let mapView = MGLMapView(frame: view.bounds)
let coordinates = CLLocationCoordinate2D(latitude: 40.4561, longitude: 49.7263)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.styleURL = MGLStyle.darkStyleURL(withVersion: 9)
mapView.setCenter(coordinates, zoomLevel: 15, animated: false)
//mapView.userTrackingMode = .followWithHeading
//mapView.showsUserHeadingIndicator = true
view.addSubview(mapView)
mapView.delegate = self
let marker = MGLPointAnnotation()
marker.coordinate = coordinates
marker.title = "Area 51"
marker.subtitle = "There is no way in!"
mapView.addAnnotation(marker)
}
// Use the default marker. See also: our view annotation or custom marker examples.
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
guard annotation is MGLPointAnnotation else {
return nil
}
let annotationAvailable = isAnnotationAvailable(annotation)
let reuseIdentifier = "\(annotationAvailable)"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if (annotationView == nil) {
annotationView = MGLAnnotationView()
annotationView = CustomAnnotationView(reuseIdentifier: reuseIdentifier)
annotationView!.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
if (!annotationAvailable) {
annotationView!.backgroundColor = UIColor(displayP3Red: 255, green: 0, blue: 0, alpha: 1)
}
else {
annotationView!.backgroundColor = UIColor(displayP3Red: 0, green: 255, blue: 0, alpha: 1)
}
}
return annotationView
}
// Allow callout view to appear when an annotation is tapped.
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
func isAnnotationAvailable(_ annotation: MGLAnnotation) -> Bool {
return false
}
}
class CustomAnnotationView: MGLAnnotationView {
override func layoutSubviews() {
super.layoutSubviews()
// Use CALayer’s corner radius to turn this view into a circle.
layer.cornerRadius = bounds.width / 2
layer.borderWidth = 2
layer.borderColor = UIColor.white.cgColor
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Animate the border width in/out, creating an iris effect.
let animation = CABasicAnimation(keyPath: "borderWidth")
animation.duration = 0.1
layer.borderWidth = selected ? bounds.width / 4 : 2
layer.add(animation, forKey: "borderWidth")
}
}
The result:
Mapbox Screenshot
I created seven different polylines. However some of them are disappearing when I zoom in closely. Why it is happening? How can I prevent this?
Here is my polyline renderer:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
renderer.strokeColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.9)
renderer.lineWidth = 2.2
return renderer
}
//Thousands of parameters sending as a parameter
func createPathWithPoints(_ points: [MKMapPoint]) {
let arc = MKPolyline(points: points, count: points.count)
mapView.addOverlay(arc)
}
Please help!
I had similar problem with using MKPolyline and below is what I did to fix this.
1) Make sure that you have your mapView delegate in viewDidLoad().
mapView.delegate = self
2) Add your overlay to the map.
mapView.addOverlay(polyLine())
I am using coredata in my project, so if myLocations are empty then I return empty MKPolyline()
private func polyLine() -> MKPolyline {
guard let locations = myLocations else {
return MKPolyline()
}
// Coordinates
let coords: [CLLocationCoordinate2D] = locations.map { location in
let location = location as! Location
return CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
}
return MKPolyline(coordinates: coords, count: coords.count)
}
3) We have access to rendererFor from MKMapViewDelegate. You can change color and width for polyline.
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let polyline = overlay as? MKPolyline else {
return MKOverlayRenderer(overlay: overlay)
}
// Setup renderer
let renderer = MKPolylineRenderer(polyline: polyline)
renderer.strokeColor = .systemBlue
renderer.lineWidth = 3
return renderer
}
I'm using Xcode 8.3.2 so first I import mapkit. Then I set markers to the map. Then I add the following code to add a polyline to the map but it won't show any.
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
self.mapView.delegate = self
super.viewDidLoad()
let template = "http://tile.openstreetmap.org/{z}/{x}/{y}.png"
let point1 = CLLocationCoordinate2D(latitude: 6.9271, longitude: 79.8612);
let point2 = CLLocationCoordinate2D(latitude: 9.6615, longitude: 80.0255);
let overlay = MKTileOverlay(urlTemplate: template)
overlay.canReplaceMapContent = true
let location = CLLocationCoordinate2DMake(6.878069, 79.892119)
mapView.add(overlay, level: .aboveLabels)
mapView.setRegion(MKCoordinateRegionMakeWithDistance(location, 1100, 1100), animated: true)
let pin = PinAnnotation(title: "Nimbus", subtitle: "Best", coordinate: location)
mapView.addAnnotation(pin)
let points: [CLLocationCoordinate2D]
points = [point1, point2]
let polyline = MKGeodesicPolyline(coordinates: points, count: 3)
mapView.add(polyline)
UIView.animate(withDuration: 1.5, animations: { () -> Void in
let span = MKCoordinateSpanMake(0.01, 0.01)
let region1 = MKCoordinateRegion(center: point1, span: span)
self.mapView.setRegion(region1, animated: true)
})
}
func mapView(_ mapview: MKMapView, rendererFor overlay: MKOverlay) ->MKOverlayRenderer{
if let overlayGeodesic = overlay as? MKGeodesicPolyline
{
let overLayRenderer = MKPolylineRenderer(polyline: overlayGeodesic)
overLayRenderer.lineWidth = 5
overLayRenderer.strokeColor = UIColor.blue
return overLayRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
First you need to add this line, I think you already have added but anyway
self.mapView.delegate = self
After that you need to implement this MKMapViewDelegate method func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer and return the MKOverlayRenderer needed for your current overlay in this case MKPolylineRenderer this is an important part if you don't implement this method then you never will have your polyline rendered
implementation will be something like this
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let overlayGeodesic = overlay as? MKGeodesicPolyline
{
let overLayRenderer = MKPolylineRenderer(polyline: overlayGeodesic)
overLayRenderer.lineWidth = 5
overLayRenderer.strokeColor = UIColor.blue
return overLayRenderer
}
if let overlayTile = overlay as? MKTileOverlay{
let overLayRenderer = MKTileOverlayRenderer(tileOverlay: overlayTile)
return overLayRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
And voila! there is your polyLine rendered
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];
}