Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Patch 3 #8

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Classes/NVMapViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
MKMapView *_mapView;
}

- (void)updatePolylineAnnotationView;

@end
50 changes: 48 additions & 2 deletions Classes/NVMapViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#import "NVMapViewController.h"
#import "NVPolylineAnnotationView.h"

#import <QuartzCore/QuartzCore.h>

@implementation NVMapViewController

Expand Down Expand Up @@ -98,7 +98,7 @@ - (void)loadView {
nil];

NVPolylineAnnotation *annotation = [[[NVPolylineAnnotation alloc] initWithPoints:points mapView:_mapView] autorelease];
[_mapView addAnnotation:annotation];
[_mapView addAnnotation:annotation];

// use some magic numbers to create a map region
MKCoordinateRegion region;
Expand All @@ -118,8 +118,54 @@ - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnno
return nil;
}

// Find any polyline annotation and update its region.
- (void)updatePolylineAnnotationView {
for (NSObject *a in [_mapView annotations]) {
if ([a isKindOfClass:[NVPolylineAnnotation class]]) {
NVPolylineAnnotation *polyline = (NVPolylineAnnotation *)a;

NSObject *pv = (NSObject *)[_mapView viewForAnnotation:polyline];
if ([pv isKindOfClass:[NVPolylineAnnotationView class]]) {
NVPolylineAnnotationView *polylineView =
(NVPolylineAnnotationView *)[_mapView viewForAnnotation:polyline];

[polylineView regionChanged];
}
}
}
}

- (void)dealloc {
[super dealloc];
}

# pragma mark - MKMapViewDelegate

- (void) mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
// fixes that some marker are behind the polyline
for (int i=0; i<[views count]; i++) {
MKAnnotationView *view = [views objectAtIndex:i];
if ([view isKindOfClass:[NVPolylineAnnotationView class]]) {
[[view superview] sendSubviewToBack:view];

/* In iOS version above 4.0 we need to update the polyline view after it
has been added to the mapview and it ready to be displayed. */
NSString *reqSysVer = @"4.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
[self updatePolylineAnnotationView];
}
}
}
}

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
/* In iOS version above 4.0 we need to update the polyline view after a region change */
NSString *reqSysVer = @"4.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
[self updatePolylineAnnotationView];
}
}

@end
5 changes: 2 additions & 3 deletions Classes/NVPolylineAnnotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
@interface NVPolylineAnnotation : NSObject<MKAnnotation> {
NSMutableArray* _points;
MKMapView* _mapView;

}

-(id) initWithPoints:(NSArray*) points mapView:(MKMapView *)mapView;

@property (nonatomic, retain) NSArray* points;

-(id) initWithPoints:(NSArray*) points mapView:(MKMapView *)mapView;

@end
4 changes: 2 additions & 2 deletions Classes/NVPolylineAnnotation.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

@implementation NVPolylineAnnotation

@synthesize points = _points;
@synthesize points = _points;

-(id) initWithPoints:(NSArray*) points mapView:(MKMapView *)mapView {
self = [super init];

_points = [[NSArray alloc] initWithArray:points];
_points = [points mutableCopy];
_mapView = [mapView retain];

return self;
Expand Down
1 change: 1 addition & 0 deletions Classes/NVPolylineAnnotationView.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@

- (id)initWithAnnotation:(NVPolylineAnnotation *)annotation
mapView:(MKMapView *)mapView;
- (void) regionChanged;

@end
135 changes: 105 additions & 30 deletions Classes/NVPolylineAnnotationView.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
// Created by William Lachance on 10-03-31.
// Inspired by code and ideas from Craig Spitzkoff and Nicolas Neubauer 2009.
//
// Further development by Joerg Polakowski (www.mobile-melting.de)

#import "NVPolylineAnnotationView.h"
#import <QuartzCore/QuartzCore.h>

const CGFloat POLYLINE_WIDTH = 4.0;

Expand Down Expand Up @@ -35,14 +37,19 @@ - (id) initWithPolylineView:(NVPolylineAnnotationView *)polylineView
self.clipsToBounds = NO;
}

// for debugging only to check when and how the polyline annotation view is updated
/*
CALayer *infoLayer = [self layer];
[infoLayer setBorderWidth:2];
[infoLayer setBorderColor:[[UIColor greenColor] CGColor]];
*/
return self;
}

-(void) drawRect:(CGRect)rect {

NVPolylineAnnotation* annotation = (NVPolylineAnnotation*)_polylineView.annotation;
if (annotation.points && annotation.points.count > 0)
{
if (!self.hidden && annotation.points && annotation.points.count > 0) {
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
Expand All @@ -55,16 +62,92 @@ -(void) drawRect:(CGRect)rect {
CLLocation* location = [annotation.points objectAtIndex:i];
CGPoint point = [_mapView convertCoordinate:location.coordinate toPointToView:self];

if (i == 0)
CGContextMoveToPoint(context, point.x, point.y);
else
CGContextAddLineToPoint(context, point.x, point.y);
}

BOOL contains = CGRectContainsPoint(rect, point);
if (contains) {
CGPoint prevPoint = CGPointZero;
@try {
CLLocation *prevLocation = [annotation.points objectAtIndex:(i - 1)];
prevPoint = [_mapView convertCoordinate:prevLocation.coordinate
toPointToView:self];
if (!CGRectContainsPoint(rect, prevPoint)) { // outside
CGContextMoveToPoint(context, prevPoint.x, prevPoint.y);
}
}
@catch(NSException *ex) {
prevPoint = CGPointZero;
}

if (!CGPointEqualToPoint(prevPoint, CGPointZero)) { // prevPoint outside
CGContextAddLineToPoint(context, point.x, point.y);
}
else { // prevPoint inside
CGContextMoveToPoint(context, point.x, point.y);
}

CGPoint nextPoint = CGPointZero;
@try {
CLLocation *nextLocation = [annotation.points objectAtIndex:(i + 1)];
nextPoint = [_mapView convertCoordinate:nextLocation.coordinate
toPointToView:self];
if (!CGRectContainsPoint(rect, nextPoint)) { // outside
CGContextAddLineToPoint(context, nextPoint.x, nextPoint.y);
}
else {
nextPoint = CGPointZero;
}
}
@catch(NSException *ex) {
nextPoint = CGPointZero;
}
}
else {
// if current point is outside the drawing rect, check if the line drawn
// between current point and next point intersects with the drawing rect
CGPoint nextPoint = CGPointZero;
@try {
CLLocation *nextLocation = [annotation.points objectAtIndex:(i + 1)];
nextPoint = [_mapView convertCoordinate:nextLocation.coordinate
toPointToView:self];
if (!CGRectContainsPoint(rect, nextPoint)) { // outside, check intersection
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathMoveToPoint(myPath, NULL, point.x, point.y );
CGPathAddLineToPoint(myPath, NULL, nextPoint.x, nextPoint.y);
CGPathCloseSubpath(myPath);

CGRect lineRect = CGPathGetBoundingBox(myPath);

if (CGRectIntersectsRect(rect, lineRect)) {
CGContextMoveToPoint(context, point.x, point.y);
CGContextAddLineToPoint(context, nextPoint.x, nextPoint.y);
}
CGPathRelease(myPath);
}
}
@catch(NSException *ex) {
nextPoint = CGPointZero;
}
}
}
CGContextStrokePath(context);
}
}

- (BOOL) lineIntersectsRect:(CGRect)rect from:(CGPoint)a to:(CGPoint)b {
float lineSlope = (b.y - a.y) / (b.x - a.x);
float yIntercept = a.y - lineSlope * a.x;
float leftY = lineSlope * CGRectGetMinX(rect) + yIntercept;
float rightY = lineSlope * CGRectGetMaxX(rect) + yIntercept;

if (leftY >= CGRectGetMinY(rect) && leftY <= CGRectGetMaxY(rect)) {
return YES;
}
if (rightY >= CGRectGetMinY(rect) && rightY <= CGRectGetMaxY(rect)) {
return YES;
}
return NO;
}


-(void) dealloc {
[super dealloc];
[_mapView release];
Expand All @@ -86,7 +169,7 @@ - (id)initWithAnnotation:(NVPolylineAnnotation *)annotation
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = NO;
self.frame = CGRectMake(0.0, 0.0, _mapView.frame.size.width, _mapView.frame.size.height);

_internalView = [[[NVPolylineInternalAnnotationView alloc] initWithPolylineView:self mapView:_mapView] autorelease];
[self addSubview:_internalView];
}
Expand All @@ -96,31 +179,23 @@ - (id)initWithAnnotation:(NVPolylineAnnotation *)annotation
-(void) regionChanged {
// move the internal route view.

NVPolylineAnnotation* annotation = (NVPolylineAnnotation*)self.annotation;
CGPoint minpt, maxpt;
for (int i = 0; i < annotation.points.count; i++)
{
CLLocation* location = [annotation.points objectAtIndex:i];
CGPoint point = [_mapView convertCoordinate:location.coordinate toPointToView:_mapView];
if (point.x < minpt.x || i == 0)
minpt.x = point.x;
if (point.y < minpt.y || i == 0)
minpt.y = point.y;
if (point.x > maxpt.x || i == 0)
maxpt.x = point.x;
if (point.y > maxpt.y || i == 0)
maxpt.y = point.y;
/* In iOS version 4.0 and above we need to calculate the new frame. Before iOS 4 setting
the view's frame to the mapview's frame was sufficient*/
NSString *reqSysVer = @"4.0";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
CGPoint origin = CGPointMake(0, 0);
origin = [_mapView convertPoint:origin toView:self];
_internalView.frame = CGRectMake(origin.x, origin.y, _mapView.frame.size.width, _mapView.frame.size.height);
}
else { // iOS < 4.0
_internalView.frame = _mapView.frame;
}

CGFloat w = maxpt.x - minpt.x + (2*POLYLINE_WIDTH);
CGFloat h = maxpt.y - minpt.y + (2*POLYLINE_WIDTH);

_internalView.frame = CGRectMake(minpt.x - POLYLINE_WIDTH, minpt.y - POLYLINE_WIDTH,
w, h);
[_internalView setNeedsDisplay];
}

- (CGPoint) centerOffset {
- (CGPoint) centerOffset {
// HACK: use the method to get the centerOffset (called by the main mapview)
// to reposition our annotation subview in response to zoom and motion
// events
Expand Down
11 changes: 11 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ MKMapView that continues to display correctly even as a user pinches and zooms
the map. It incorporates and expands upon ideas from Craig Spitzkoff and
Nicolas Neubauer.

This solution is focused on iPhone OS versions pro to 4.x
Note: In iPhone OS 4.0 the MKPolyline has been added.

However, if you want to create an application, which should run on iOS versions prior to 4.x and make use of a polyline, you should use this project's polyline implementation.

For more information, see:

http://navarra.ca/?p=786
Expand All @@ -11,3 +16,9 @@ For information on the inspiration behind this original work, see:

http://spitzkoff.com/craig/?p=65
http://pixelfehler.nicolas-neubauer.de/2009/09/04/mapkit-google-maps-iphone-and-drawing-routes-or-polylines/

The original example from http://github.com/wlach contained some bugs, which did not seem to get fixed. Bugs fixed:
- Polyline View overlaps other Annotation Views (http://github.com/wlach/nvpolyline/issues#issue/2)
- App crashes when zoomed in (http://github.com/wlach/nvpolyline/issues#issue/1)


7 changes: 7 additions & 0 deletions nvpolyline.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
1FF5265011FEC1540018A36F /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF5264F11FEC1540018A36F /* QuartzCore.framework */; };
288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; };
/* End PBXBuildFile section */
Expand All @@ -34,6 +35,7 @@
1D3623250D0F684500981E51 /* nvpolylineAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = nvpolylineAppDelegate.m; sourceTree = "<group>"; };
1D6058910D05DD3D006BFB54 /* nvpolyline.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = nvpolyline.app; sourceTree = BUILT_PRODUCTS_DIR; };
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
1FF5264F11FEC1540018A36F /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = iphoneos3.1.3/System/Library/Frameworks/QuartzCore.framework; sourceTree = "<group>"; };
288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = "<group>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
Expand All @@ -51,6 +53,7 @@
288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */,
069D56261162FB1B009B3FF2 /* MapKit.framework in Frameworks */,
069D57F81164E2A3009B3FF2 /* CoreLocation.framework in Frameworks */,
1FF5265011FEC1540018A36F /* QuartzCore.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -113,6 +116,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
1FF5264F11FEC1540018A36F /* QuartzCore.framework */,
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
1D30AB110D05D00D00671497 /* Foundation.framework */,
288765FC0DF74451002DB57D /* CoreGraphics.framework */,
Expand Down Expand Up @@ -190,13 +194,16 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Don't Code Sign";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = nvpolyline_Prefix.pch;
INFOPLIST_FILE = "nvpolyline-Info.plist";
PRODUCT_NAME = nvpolyline;
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
SDKROOT = iphoneos4.0;
};
name = Debug;
};
Expand Down