Updating and organizing overlays on an MKMapView - ios

I want to do overlays on an MKMapView. So I created my custom object:
#interface Spot : NSObject
#property int spot_id;
#property CLLocationCoordinate2D coordinate;
#property float radius;
#property NSObject<MKOverlay> * overlay;
- (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(float)radius;
#end
There is a backend, that constantly updates the Spots and delivers them to the phone. So I have to:
Check if the currently rendered spots are still valid and there
If yes, have they moved or changed radius? If so, update them (move? / delete + redraw?!)
Delete if they don't exist anymore.
Is there a better way of keeping track of the overlays? Keeping an NSObject<MKoverlay> * reference attached to each spot and constantly reassigning it feels a bit strange to me.

It seems like there are three questions here:
How to detect changes in spots in your server?
The ugly way to do this would be iterate through your spots and see if the coordinate and/or radius changed. A little better would be to have the server update create, modify, and delete timestamps or some other identifier so the client would have the ability to retrieve all creations, modifications, and/or deletions since the last update. Best would be to marry that with some form of push notification, so the client would also be proactively notified of these changes.
This question is difficult to answer in the abstract. It depends a lot upon the capabilities of your server and the nature of the database (e.g. how many "spots", how frequently do they change, etc.). This impacts both the client-server architecture, but also the client implementation.
How to update the map when a spot changes?
This is a far easier question. Insertions and deletions are easy. You just do addOverlay: (or, in iOS 7, addOverlay:level: and removeOverlay:. For updates, while it's inelegant, I think the easiest way is to just remove the old overlay and add it back in and viewForOverlay will take care of the user interface for you.
What is the right structure of the Spot class?
Just as a thought, but it seems duplicative to have your coordinate and radius properties, and then have a id<MKOverlay> overlay object too (because that's presumably a MKCircle with the same two properties). If your overlays are going to be MKCircle objects, it might be easier to just have a Spot class, itself, to conform to MKOverlay:
#interface Spot : NSObject <MKOverlay>
#property (nonatomic) int spot_id;
#property (nonatomic) CLLocationCoordinate2D coordinate;
#property (nonatomic) CLLocationDistance radius;
#property (nonatomic, readonly) MKMapRect boundingMapRect;
- (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDistance)radius;
#end
Then, all you need to do is to implement boundingMapRect and intersectsMapRect:
- (MKMapRect) boundingMapRect
{
MKMapPoint point = MKMapPointForCoordinate(self.coordinate);
CLLocationDistance distance = self.radius * MKMetersPerMapPointAtLatitude(self.coordinate.latitude);
MKMapRect rect = MKMapRectMake(point.x, point.y, distance * 2.0, distance * 2.0);
rect = MKMapRectOffset(rect, -distance, -distance);
return rect;
}
- (BOOL)intersectsMapRect:(MKMapRect)mapRect
{
return MKMapRectIntersectsRect(mapRect, [self boundingMapRect]);
}
You might want to double-check that boundingMapRect logic, but I think it's right.
Then you can just add and remove Spot objects as overlays themselves. And all you need to do is to implement a viewForOverlay in your MKMapViewDelegate, for example, in iOS versions prior to 7, that would be:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[Spot class]])
{
Spot *spot = (id)overlay;
MKCircle *circle = [MKCircle circleWithCenterCoordinate:spot.coordinate
radius:spot.radius];
MKCircleView *overlayView = [[MKCircleView alloc] initWithCircle:circle];
overlayView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
overlayView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
overlayView.lineWidth = 3.0;
return overlayView;
}
return nil;
}
In iOS 7, that would be:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[Spot class]])
{
Spot *spot = (id)overlay;
MKCircle *circle = [MKCircle circleWithCenterCoordinate:spot.coordinate
radius:spot.radius];
MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithCircle:circle];
renderer.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
renderer.lineWidth = 3;
return renderer;
}
return nil;
}
Another approach would be to define your Spot as:
#interface Spot : NSObject <MKOverlay>
#property (nonatomic) int spot_id;
#property (nonatomic, strong) MKCircle *overlay;
- (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDistance)radius;
And you could then just define boundingMapRect and coordinate return the appropriate values from the MKCircle (saving you from having to write your own):
- (MKMapRect)boundingMapRect
{
return [self.circle boundingMapRect];
}
- (CLLocationCoordinate2D)coordinate
{
return [self.circle coordinate];
}
Clearly the init method would change:
- (id) initWithSpotId:(int)spot_id position:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDistance)radius;
{
self = [super init];
if (self) {
_spot_id = spot_id;
_circle = [MKCircle circleWithCenterCoordinate:coordinate radius:radius];
}
return self;
}
As would the viewForOverlay (iOS versions prior to 7.0) in the MKMapViewDelegate:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[SpotCircle class]])
{
SpotCircle *spot = (id)overlay;
MKCircleView *overlayView = [[MKCircleView alloc] initWithCircle:spot.circle];
overlayView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
overlayView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
overlayView.lineWidth = 3.0;
return overlayView;
}
return nil;
}
In iOS 7, that would be:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[SpotCircle class]])
{
SpotCircle *spot = (id)overlay;
MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithCircle:spot.circle];
renderer.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
renderer.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.7];
renderer.lineWidth = 3;
return renderer;
}
return nil;
}
I hope that helps.

Related

overlay map view not showing up

I'm trying to make a MKMapView and add and overlay to it using coordinates.
It is not showing up in the mapView.
it seems to be compiling ok but when I open the mapView view in the storyboard I see the map of the world but there is no red overlay anywhere.
Here is my code.
my header
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreMotion/CoreMotion.h>
#import <MapKit/MapKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface mapViewController : UIViewController<CLLocationManagerDelegate,MKMapViewDelegate>
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#property(nonatomic, retain) MKPolygon *polygon;
#end
and my .m file
#import "mapViewController.h"
#interface mapViewController ()
#end
#implementation mapViewController
#synthesize mapView,locationManager,polygon;
- (void)viewDidLoad {
[super viewDidLoad];
mapView.delegate = self;
NSDictionary *d1=[NSDictionary dictionaryWithObjectsAndKeys:#"37.57111111",#"x",#"-109.52166667",#"y",nil];
NSDictionary *d2=[NSDictionary dictionaryWithObjectsAndKeys:#"32.01138889",#"x",#"-109.59000000",#"y",nil];
NSDictionary *d3=[NSDictionary dictionaryWithObjectsAndKeys:#"31.83166667",#"x",#"-111.95416667",#"y",nil];
NSDictionary *d4=[NSDictionary dictionaryWithObjectsAndKeys:#"32.34250000",#"x",#"-115.14333333",#"y",nil];
NSDictionary *d5=[NSDictionary dictionaryWithObjectsAndKeys:#"34.89722222",#"x",#"-115.47611111",#"y",nil];
NSDictionary *d6=[NSDictionary dictionaryWithObjectsAndKeys:#"37.32083333",#"x",#"-116.02027778",#"y",nil];
NSDictionary *d7=[NSDictionary dictionaryWithObjectsAndKeys:#"37.53305556",#"x",#"-114.73416667",#"y",nil];
NSDictionary *d8=[NSDictionary dictionaryWithObjectsAndKeys:#"37.53166667",#"x",#"-114.11277778",#"y",nil];
NSDictionary *d9=[NSDictionary dictionaryWithObjectsAndKeys:#"37.57111111",#"x",#"-109.52166667",#"y",nil];
NSArray *azcoord = [NSArray arrayWithObjects:d1,d2,d3,d4,d5,d6,d7,d8, nil];
CLLocationCoordinate2D coordinates[azcoord.count];
int coordinatesIndex = 0;
for (NSDictionary * c in azcoord) {
double x = [[c valueForKey:#"x"] doubleValue];
double y = [[c valueForKey:#"y"] doubleValue];
CLLocationCoordinate2D coordinate;
coordinate.latitude = y;
coordinate.longitude = x;
//Put this coordinate in the C array...
coordinates[coordinatesIndex] = coordinate;
coordinatesIndex++;
}
polygon = [MKPolygon polygonWithCoordinates:coordinates count:azcoord.count];
[self.mapView addOverlay:polygon];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:polygon];
renderer.fillColor = [[UIColor redColor] colorWithAlphaComponent:1];
renderer.strokeColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
renderer.lineWidth = 2;
return renderer;
}
#end
what I am trying to do is draw an overlay for the state of Arizona. I've checked the coordinates and they are correct.
Eventually I want to create overlays for all states.
can anyone see where I'm going wrong?
I figured it out by changing my approach. Here is my code that is working.
first i create a CLLocationCoordinate2D array of my coordinates and make them into a polygon. then add to mapView.
CLLocationCoordinate2D points[7];
points [0] = CLLocationCoordinate2DMake(36.99910000, -109.04520000);
points [1] = CLLocationCoordinate2DMake(31.33460000, -109.05220000);
points [2] = CLLocationCoordinate2DMake(31.33700000, -111.07720000);
points [3] = CLLocationCoordinate2DMake(32.49410000, -114.81360000);
points [4] = CLLocationCoordinate2DMake(36.10660000, -114.75180000);
points [5] = CLLocationCoordinate2DMake(36.21600000, -114.03940000);
points [6] = CLLocationCoordinate2DMake(37.00150000, -114.04820000);
polygon = [MKPolygon polygonWithCoordinates:points count:7];
[self.mapView addOverlay:polygon];
then I render that to the map view.
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{ NSLog(#"YES!!!!!!!!!!!!!!!!!");
MKPolygonRenderer *renderer = [[MKPolygonRenderer alloc] initWithPolygon:polygon];
renderer.fillColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
renderer.strokeColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
renderer.lineWidth = 1;
return renderer;
}
then I check to see if my location is inside the polygon.
CLLocationCoordinate2D sampleLocation = CLLocationCoordinate2DMake(myLatitude,myLongitude);//13,80 is the latlong of clear colored area of the MKPolygon in the below image.
MKMapPoint mapPoint = MKMapPointForCoordinate(sampleLocation);
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
for (id<MKOverlay> polygon in mapView.overlays) {
if([polygon isKindOfClass:[MKPolygon class]]){
NSLog(#"YES<<<<<<<<<<<<<<<<<<");
MKPolygon *polygons = (MKPolygon*) polygon;
CGMutablePathRef mpr = CGPathCreateMutable();
MKMapPoint *polygonPoints = polygons.points;
for (int p=0; p < polygons.pointCount; p++){
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}
if(CGPathContainsPoint(mpr , NULL, mapPointAsCGP, TRUE)){
NSLog(#"YES?????????????????????????????????");
//do something else
}
CGPathRelease(mpr);
}
}
I at first didn't want to add a map view but then decided I need to and now that I have I like it.

Draw Custom shapes (Geofences) zones on MKMapView

I'm creating an Universal app and I have to create custom safe zones on the map view.
What I do is:
Add a new UIView as superview of map view called squareZone.
To the squareZone view I add UIPanGestureRecognizer, UIPinchGestureRecognizer and UIRotationGestureRecognizer so I can move, rotate and zoom (in and out).
Here is the code of SquareZone
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.opaque = NO;
self.layer.cornerRadius = 5.0f;
}
return self;
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
UIColor *color = [UIColor colorWithRed: 0.765 green: 0.439 blue: 0.443 alpha: 0.658];
UIBezierPath *rectanglePath = [UIBezierPath bezierPathWithRect: rect];
[color setFill];
[rectanglePath fill];
[UIColor.whiteColor setStroke];
rectanglePath.lineWidth = 5;
CGFloat rectanglePattern[] = {2, 3};
[rectanglePath setLineDash: rectanglePattern count: 2 phase: 0];
[rectanglePath stroke];
}
Now, when the user adjust the squareZone I have to show on a UILabel the distance between each point in meters. For that task I'm using
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
How can I add/show the four UILabels when the user interacts with the squareZone.
I need some light here. I had seen many tutorials but I cannot imagine how can this is posible. For reference, there is an app called
Trax: https://itunes.apple.com/us/app/trax-gps-tracker/id647170688?mt=8
I have to do the same Drawing Geofence Zone.
Thanks in advance.
First thing to mention is that Trax has two different modes:
1) editing -- where you draw the path
2) live -- where it shows the result.
Editing mode
While in editing, you cannot move and zoom inside of your MKMapView. This happens because they are using the same approach as you do -- they are adding a UIView on top of MKMapView, so that gestures do not conflict with each other.
All you need to do is add CGPoints to some array and use them in the future.
Of course, there are a few difficulties with using CoreGraphics framework, but it is not that tricky.
Live mode
After user added all CGPoints, you now have to convert these points into actual CLLocationCoordinate2D instances.
CGPoint thePoint = ...
CLLocationCoordinate2D theLocationCoordinate2D = [theMapView convertPoint:thePoint toCoordinateFromView:theDrawingView];
What Trax have in their app are probably (almost certainly) instances of MKPolygon class.
You can add it like so:
NSUInteger theCount = self.theMainDrawingView.thePointsArray.count;
CLLocationCoordinate2D theLocationCoordinatesArray[theCount];
for (NSUInteger theIndex = 0; theIndex < theCount; theIndex++)
{
CGPoint thePoint = self.theMainDrawingView.thePointsArray[theIndex].CGPointValue;
CLLocationCoordinate2D theLocationCoordinate2D = [self.theMainMapView convertPoint:thePoint toCoordinateFromView:self.theMainDrawingView];
theLocationCoordinatesArray[theIndex] = theLocationCoordinate2D;
}
MKPolygon *thePolygon = [MKPolygon polygonWithCoordinates:theLocationCoordinatesArray count:theCount];
[self.theMainMapView addOverlay:thePolygon];
However, this is not the end. This will trigger a delegate method of your MKMapView (don't forget to set its .delegate property)
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if (![overlay isKindOfClass:[MKPolygon class]])
{
return nil;
}
MKPolygonView *thePolygonView = [[MKPolygonView alloc] initWithPolygon:(MKPolygon *)overlay];
thePolygonView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
thePolygonView.strokeColor = [[UIColor redColor] colorWithAlphaComponent:0.6];
thePolygonView.lineWidth = 4;
return thePolygonView;
}
Result looks like this:
Summary
Of course, this doesn't fully solve the issue, because you would have to add pinch and pan gestures too, but I hope that I could point into the right direction.

Not working drawing on MapKit?

I'm new to iOS programming. I am trying to draw one line between two points not more than one line. This is what I tried so far;
#property (nonatomic, strong) MKPolylineView *lineView;
#property (nonatomic, strong) MKPolyline *line;
Create a polyline all location ;
-(void)allLocationRoute {
CLLocationCoordinate2D coordinates[record.toMoments.count];
int i = 0;
for (MomentEntity *currentEntity in record.toMoments.allObjects) {
coordinates[i].latitude = [currentEntity.latitude doubleValue];
coordinates[i].longitude = [currentEntity.longitude doubleValue];
i++;
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coordinates count:record.toMoments.allObjects.count];
[self.mapView addOverlay:polyline];
line = polyline;
}
MapView:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay {
if ([overlay isKindOfClass:[MKPolyline class]]) {
self.lineView = [[MKPolylineView alloc]initWithPolyline:(MKPolyline*)overlay] ;
self.lineView.strokeColor = [[UIColor redColor] colorWithAlphaComponent:1];
self.lineView.lineWidth = 1;
return self.lineView;
}
return nil;
}
But as you can see from first picture red line makes zig-zag. Any help appreciated.

dynamically change lineWidth of mkpolyline

I have Map in my app in which is moving as per user location.I have successfully drawn a polyline for source and destination.
Using following code
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id < MKOverlay >)overlay{
MKPolylineView *view1 = [[MKPolylineView alloc] initWithOverlay:overlay];
view1.lineWidth = 27.0;
view1.strokeColor = [UIColor colorWithRed:55.0/255.0 green:168.0/255.0 blue:219.0/255.0 alpha:1];
return view1;
}
But my problem is when map is moving , mean i am changing region of map as user moves , then some times polyline is not shown good some time its show thicker than actual size as you can see in below image.
i am attaching the image below, please let me know what can i do for smooth polyline when map is moving.
EDIT
As Matt suggested i create a subclass of MKPolylineRenderer and implement drawMapRect method as below:
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context{
CGMutablePathRef fullPath = CGPathCreateMutable();
BOOL pathIsEmpty = YES;
//merging all the points as entire path
for (int i=0;i< self.polyline.pointCount;i++){
CGPoint point = [self pointForMapPoint:self.polyline.points[i]];
if (pathIsEmpty){
CGPathMoveToPoint(fullPath, nil, point.x, point.y);
pathIsEmpty = NO;
} else {
CGPathAddLineToPoint(fullPath, nil, point.x, point.y);
}
}
//get bounding box out of entire path.
CGRect pointsRect = CGPathGetBoundingBox(fullPath);
CGRect mapRectCG = [self rectForMapRect:mapRect];
//stop any drawing logic, cuz there is no path in current rect.
if (!CGRectIntersectsRect(pointsRect, mapRectCG))return;
UIColor *darker = [UIColor blackColor];
CGFloat baseWidth = 10 / zoomScale;
// draw the dark colour thicker
CGContextAddPath(context, self.path);
CGContextSetStrokeColorWithColor(context, darker.CGColor);
CGContextSetLineWidth(context, baseWidth * 1.5);
CGContextSetLineCap(context, self.lineCap);
CGContextStrokePath(context);
// now draw the stroke color with the regular width
CGContextAddPath(context, self.path);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, baseWidth);
CGContextSetLineCap(context, self.lineCap);
CGContextStrokePath(context);
[super drawMapRect:mapRect zoomScale:zoomScale inContext:context];
}
But Same problem
See below image:
ok, i think i got problem , my polyline is added once, but as user's speed i changing zoom level of MKMapView, zoom level is changed , but polyline width is not refresh,
SO how can i dynamically change lineWidth of mkpolyline ??
The problem is that you are setting your lineWidth at a fixed width (a really big fixed width). This gets larger or smaller as the map is zoomed larger or smaller.
Instead, implement your own MKOverlayRenderer subclass, and override drawMapRect:zoomScale:inContext:. It receives a zoomScale parameter, and so you can adjust the width of your line to look good at what the current scale may be.
Subclass MKPolylineRenderer and override applyStrokePropertiesToContext:atZoomScale: so that it ignores the scale, and draws lines at constant width:
#interface ConstantWidthPolylineRenderer : MKPolylineRenderer
#end
#implementation ConstantWidthPolylineRenderer
- (void)applyStrokePropertiesToContext:(CGContextRef)context
atZoomScale:(MKZoomScale)zoomScale
{
[super applyStrokePropertiesToContext:context atZoomScale:zoomScale];
CGContextSetLineWidth(context, self.lineWidth);
}
#end
Now use it and admire its smooth rendering:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
MKPolyline *polyline = (MKPolyline *)overlay;
ConstantWidthPolylineRenderer *renderer = [[ConstantWidthPolylineRenderer alloc] initWithPolyline:polyline];
renderer.strokeColor = [UIColor redColor];
renderer.lineWidth = 40;
return renderer;
}
You need to use MKPolylineRenderer instead of MKPolylineView
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id < MKOverlay >)overlay {
#try {
MKPolylineRenderer *renderer = [[[MKPolylineRenderer alloc] initWithOverlay:overlay];
renderer.lineWidth = 27.0;
renderer.strokeColor = [UIColor colorWithRed:55.0/255.0 green:168.0/255.0 blue:219.0/255.0 alpha:1];
return renderer;
}
#catch (NSException *exception) {
NSLog(#"exception :%#",exception.debugDescription);
}
}
Read this one:
The MKPolylineRenderer class provides the visual representation for an MKPolyline overlay object. This renderer strokes the line only; it does not fill it. You can change the color and other drawing attributes of the polygon by modifying the properties inherited from the parent class. You typically use this class as is and do not subclass it.
From Apple library
The MKPolylineView class provides the visual representation for an MKPolyline annotation object. This view strokes the path represented by the annotation. (This class does not fill the area enclosed by the path.) You can change the color and other drawing attributes of the path by modifying the properties inherited from the MKOverlayPathView class. This class is typically used as is and not subclassed.
From Apple library

mkmapview image overlay zooming Memory issue in ios

When I am adding a imageOverlay with high resolution image and map zooms image in the map gone and got memory issues.some times my app crashes. How can this be solved?
Here is my code
-(void)addOverlay
{
MapOverlay *overlay = [[MapOverlay alloc] initWithRouteOverlay:self.routeOverlayObj];
[self.mapView addOverlay:overlay];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if([overlay isKindOfClass:[MapOverlay class]])
{
mapOverlay = (MapOverlay *)overlay;
NSLog(#"%# %#",routeInfoObj.routeOverlay,[UIImage imageNamed:routeInfoObj.routeOverlay]);
ATTMapOverLayRender *mapOverlayRender = [[ATTMapOverLayRender alloc] initWithOverlay:mapOverlay overlayImage:[UIImage imageNamed:routeInfoObj.routeOverlay] andImageFile:[NSString stringWithFormat:#"%#.png",routeInfoObj.routeOverlay]];
return mapOverlayRender;
}
else
{
MKPolylineRenderer *routeRenderer = [[MKPolylineRenderer alloc] initWithPolyline:routePolyline];
routeRenderer.strokeColor = [UIColor redColor];
routeRenderer.fillColor = [UIColor redColor];
routeRenderer.lineWidth = 5;
return routeRenderer;
}
return nil;
}
and in "ATTMApOVerlayRender.h"
#interface ATTMapOverLayRender : MKOverlayRenderer
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage andImageFile:(NSString *)file;
#property (nonatomic, weak) UIImage *overlayImage;
#end
"ATTMApOVerlayRender.h"
#import "ATTMapOverLayRender.h"
#implementation ATTMapOverLayRender
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage andImageFile:(NSString *)file {
self = [super initWithOverlay:overlay];
if (self) {
self.overlayImage = overlayImage;
}
return self;
}
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
CGImageRef image = _overlayImage.CGImage;
MKMapRect overlayMapRect = [self.overlay boundingMapRect];
CGRect overlayRect = [self rectForMapRect:overlayMapRect];
// draw image
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0.0, -overlayRect.size.height);
CGContextDrawImage(context, overlayRect, image);
}
#end
I'm running into similar problems on a tile overlay project as well.
I was able to significantly improve the memory usage by maintaining a single MKMapOverlayRenderer.
Rather than allocating memory for a new MKOverlayRenderer (or MKPolylineRenderer) every time the mapView: renderForOverlay: method is called, create a class variable and rewrite to it.
So in your ViewController.h or whatever class contains your mapView that calls -(MKOverlayRenderer*)mapView:(MKMapView*)mapView rendererForOverlay:(id<MKOverlay>)overlay have,
#property (nonatomic, strong) MKOverlayRenderer *renderer;
And in the declaration of -(MKOverlayRenderer*)mapView:(MKMapView*)mapView rendererForOverlay:(id<MKOverlay>)overlay
use
self.renderer = [[MKOverlayRenderer alloc] init...

Resources