Authored by Justin R. Miller

Merge branch 'develop' of github.com:mapbox/mapbox-ios-sdk into develop

... ... @@ -34,7 +34,7 @@
@interface RMAbstractWebMapSource : RMAbstractMercatorTileSource
@property (nonatomic, assign) NSUInteger retryCount;
@property (nonatomic, assign) NSTimeInterval waitSeconds;
@property (nonatomic, assign) NSTimeInterval requestTimeoutSeconds;
- (NSURL *)URLForTile:(RMTile)tile;
... ...
... ... @@ -28,9 +28,11 @@
#import "RMAbstractWebMapSource.h"
#import "RMTileCache.h"
#define HTTP_404_NOT_FOUND 404
@implementation RMAbstractWebMapSource
@synthesize retryCount, waitSeconds;
@synthesize retryCount, requestTimeoutSeconds;
- (id)init
{
... ... @@ -38,7 +40,7 @@
return nil;
self.retryCount = RMAbstractWebMapSourceDefaultRetryCount;
self.waitSeconds = RMAbstractWebMapSourceDefaultWaitSeconds;
self.requestTimeoutSeconds = RMAbstractWebMapSourceDefaultWaitSeconds;
return self;
}
... ... @@ -74,6 +76,8 @@
NSArray *URLs = [self URLsForTile:tile];
if ([URLs count] > 1)
{
// fill up collection array with placeholders
//
NSMutableArray *tilesData = [NSMutableArray arrayWithCapacity:[URLs count]];
... ... @@ -94,7 +98,7 @@
for (NSUInteger try = 0; tileData == nil && try < self.retryCount; ++try)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:currentURL];
[request setTimeoutInterval:(self.waitSeconds / (CGFloat)self.retryCount)];
[request setTimeoutInterval:(self.requestTimeoutSeconds / (CGFloat)self.retryCount)];
tileData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
}
... ... @@ -112,7 +116,7 @@
// wait for whole group of fetches (with retries) to finish, then clean up
//
dispatch_group_wait(fetchGroup, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * self.waitSeconds));
dispatch_group_wait(fetchGroup, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * self.requestTimeoutSeconds));
dispatch_release(fetchGroup);
// composite the collected images together
... ... @@ -136,6 +140,20 @@
}
}
}
}
else
{
for (NSUInteger try = 0; image == nil && try < self.retryCount; ++try)
{
NSHTTPURLResponse *response = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[URLs objectAtIndex:0]];
[request setTimeoutInterval:(self.requestTimeoutSeconds / (CGFloat)self.retryCount)];
image = [UIImage imageWithData:[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil]];
if (response.statusCode == HTTP_404_NOT_FOUND)
break;
}
}
if (image)
[tileCache addImage:image forTile:tile withCacheKey:[self uniqueTilecacheKey]];
... ...
... ... @@ -74,7 +74,7 @@
@property (nonatomic, assign) RMQuadTreeNode *quadTreeNode;
// This is for filtering framework-provided annotations.
@property (nonatomic, assign, readonly) BOOL isUserLocationAnnotation;
@property (nonatomic, readonly) BOOL isUserLocationAnnotation;
#pragma mark -
... ...
... ... @@ -76,6 +76,8 @@
self.enabled = YES;
self.clusteringEnabled = YES;
self.isUserLocationAnnotation = NO;
layer = nil;
return self;
... ...
... ... @@ -12,6 +12,9 @@
#import "RMTileSource.h"
@implementation RMAttributionViewController
{
RMMapView *_mapView;
}
- (id)initWithMapView:(RMMapView *)mapView
{
... ... @@ -24,6 +27,8 @@
if (self)
{
_mapView = mapView;
self.view.backgroundColor = [UIColor darkGrayColor];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissModalViewControllerAnimated:)]];
... ... @@ -32,6 +37,8 @@
webView.delegate = self;
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
webView.backgroundColor = [UIColor clearColor];
webView.opaque = NO;
... ... @@ -56,6 +63,11 @@
return self;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return [_mapView.viewControllerPresentingAttribution shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
#pragma mark -
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
... ...
... ... @@ -88,12 +88,7 @@ typedef enum : NSUInteger {
@property (nonatomic, assign) BOOL adjustTilesForRetinaDisplay;
@property (nonatomic, readonly) float adjustedZoomForRetinaDisplay; // takes adjustTilesForRetinaDisplay and screen scale into account
@property (nonatomic) BOOL showsUserLocation;
@property (nonatomic, readonly, retain) RMUserLocation *userLocation;
@property (nonatomic, readonly, getter=isUserLocationVisible) BOOL userLocationVisible;
@property (nonatomic) RMUserTrackingMode userTrackingMode;
@property (weak) UIViewController *viewControllerPresentingAttribution;
@property (nonatomic, assign) UIViewController *viewControllerPresentingAttribution;
// take missing tiles from lower zoom levels, up to #missingTilesDepth zoom levels (defaults to 0, which disables this feature)
@property (nonatomic, assign) NSUInteger missingTilesDepth;
... ... @@ -250,6 +245,12 @@ typedef enum : NSUInteger {
#pragma mark -
#pragma mark User Location
@property (nonatomic, assign) BOOL showsUserLocation;
@property (nonatomic, readonly) RMUserLocation *userLocation;
@property (nonatomic, readonly, getter=isUserLocationVisible) BOOL userLocationVisible;
@property (nonatomic, assign) RMUserTrackingMode userTrackingMode;
@property (nonatomic, assign) BOOL displayHeadingCalibration;
- (void)setUserTrackingMode:(RMUserTrackingMode)mode animated:(BOOL)animated;
@end
... ...
... ... @@ -75,6 +75,8 @@
- (void)correctMinZoomScaleForBoundingMask;
- (void)updateHeadingForDeviceOrientation;
@end
#pragma mark -
... ... @@ -160,8 +162,13 @@
UIImageView *userLocationTrackingView;
UIImageView *userHeadingTrackingView;
UIViewController *viewControllerPresentingAttribution;
UIButton *attributionButton;
UIViewController *_viewControllerPresentingAttribution;
UIButton *_attributionButton;
BOOL _userAlteringPanOrZoom;
CGAffineTransform _mapTransform;
CATransform3D _annotationTransform;
}
@synthesize decelerationMode = _decelerationMode;
... ... @@ -176,7 +183,7 @@
@synthesize positionClusterMarkersAtTheGravityCenter = _positionClusterMarkersAtTheGravityCenter;
@synthesize clusterMarkerSize = _clusterMarkerSize, clusterAreaSize = _clusterAreaSize;
@synthesize adjustTilesForRetinaDisplay = _adjustTilesForRetinaDisplay;
@synthesize userLocation, showsUserLocation, userTrackingMode;
@synthesize userLocation, showsUserLocation, userTrackingMode, displayHeadingCalibration;
@synthesize missingTilesDepth = _missingTilesDepth;
@synthesize debugTiles = _debugTiles;
... ... @@ -240,11 +247,26 @@
[self setDecelerationMode:RMMapDecelerationFast];
[self setBoundingMask:RMMapMinHeightBound];
self.displayHeadingCalibration = YES;
_mapTransform = CGAffineTransformIdentity;
_annotationTransform = CATransform3DIdentity;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMemoryWarningNotification:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleWillChangeOrientationNotification:)
name:UIApplicationWillChangeStatusBarOrientationNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleDidChangeOrientationNotification:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
RMLog(@"Map initialised. tileSource:%@, minZoom:%f, maxZoom:%f, zoom:%f at {%f,%f}", newTilesource, self.minZoom, self.maxZoom, self.zoom, initialCenterCoordinate.longitude, initialCenterCoordinate.latitude);
}
... ... @@ -361,7 +383,7 @@
[userLocation release]; userLocation = nil;
[userLocationTrackingView release]; userLocationTrackingView = nil;
[userHeadingTrackingView release]; userHeadingTrackingView = nil;
[attributionButton release]; attributionButton = nil;
[_attributionButton release]; _attributionButton = nil;
[super dealloc];
}
... ... @@ -378,6 +400,19 @@
[self didReceiveMemoryWarning];
}
- (void)handleWillChangeOrientationNotification:(NSNotification *)notification
{
// send a dummy heading update to force re-rotation
//
if (userTrackingMode == RMUserTrackingModeFollowWithHeading)
[self locationManager:locationManager didUpdateHeading:locationManager.heading];
}
- (void)handleDidChangeOrientationNotification:(NSNotification *)notification
{
[self updateHeadingForDeviceOrientation];
}
- (NSString *)description
{
CGRect bounds = self.bounds;
... ... @@ -400,13 +435,13 @@
_delegate = aDelegate;
_delegateHasBeforeMapMove = [_delegate respondsToSelector:@selector(beforeMapMove:)];
_delegateHasAfterMapMove = [_delegate respondsToSelector:@selector(afterMapMove:)];
_delegateHasBeforeMapMove = [_delegate respondsToSelector:@selector(beforeMapMove:byUser:)];
_delegateHasAfterMapMove = [_delegate respondsToSelector:@selector(afterMapMove:byUser:)];
_delegateHasBeforeMapZoom = [_delegate respondsToSelector:@selector(beforeMapZoom:)];
_delegateHasAfterMapZoom = [_delegate respondsToSelector:@selector(afterMapZoom:)];
_delegateHasBeforeMapZoom = [_delegate respondsToSelector:@selector(beforeMapZoom:byUser:)];
_delegateHasAfterMapZoom = [_delegate respondsToSelector:@selector(afterMapZoom:byUser:)];
_delegateHasMapViewRegionDidChange = [_delegate respondsToSelector:@selector(mapViewRegionDidChange:)];
_delegateHasMapViewRegionDidChange = [_delegate respondsToSelector:@selector(mapViewRegionDidChange:byUser:)];
_delegateHasDoubleTapOnMap = [_delegate respondsToSelector:@selector(doubleTapOnMap:at:)];
_delegateHasSingleTapOnMap = [_delegate respondsToSelector:@selector(singleTapOnMap:at:)];
... ... @@ -593,7 +628,7 @@
- (void)setCenterProjectedPoint:(RMProjectedPoint)centerProjectedPoint animated:(BOOL)animated
{
if (_delegateHasBeforeMapMove)
[_delegate beforeMapMove:self];
[_delegate beforeMapMove:self byUser:_userAlteringPanOrZoom];
// RMLog(@"Current contentSize: {%.0f,%.0f}, zoom: %f", mapScrollView.contentSize.width, mapScrollView.contentSize.height, self.zoom);
... ... @@ -609,7 +644,7 @@
// RMLog(@"setMapCenterProjectedPoint: {%f,%f} -> {%.0f,%.0f}", centerProjectedPoint.x, centerProjectedPoint.y, mapScrollView.contentOffset.x, mapScrollView.contentOffset.y);
if (_delegateHasAfterMapMove && !animated)
[_delegate afterMapMove:self];
[_delegate afterMapMove:self byUser:_userAlteringPanOrZoom];
[self correctPositionOfAllAnnotations];
}
... ... @@ -619,7 +654,7 @@
- (void)moveBy:(CGSize)delta
{
if (_delegateHasBeforeMapMove)
[_delegate beforeMapMove:self];
[_delegate beforeMapMove:self byUser:_userAlteringPanOrZoom];
CGPoint contentOffset = _mapScrollView.contentOffset;
contentOffset.x += delta.width;
... ... @@ -627,7 +662,7 @@
_mapScrollView.contentOffset = contentOffset;
if (_delegateHasAfterMapMove)
[_delegate afterMapMove:self];
[_delegate afterMapMove:self byUser:_userAlteringPanOrZoom];
}
#pragma mark -
... ... @@ -824,7 +859,7 @@
- (void)zoomInToNextNativeZoomAt:(CGPoint)pivot animated:(BOOL)animated
{
if (self.userTrackingMode != RMUserTrackingModeNone && ! CGPointEqualToPoint(pivot, self.center))
if (self.userTrackingMode != RMUserTrackingModeNone && ! CGPointEqualToPoint(pivot, [self coordinateToPixel:userLocation.location.coordinate]))
self.userTrackingMode = RMUserTrackingModeNone;
// Calculate rounded zoom
... ... @@ -935,9 +970,6 @@
myOrigin.y = myOrigin.y - (zoomRect.size.height / 2);
zoomRect.origin = myOrigin;
RMProjectedPoint topRight = RMProjectedPointMake(myOrigin.x + zoomRect.size.width, myOrigin.y + zoomRect.size.height);
RMLog(@"zoomWithBoundingBox: {%f,%f} - {%f,%f}", [_projection projectedPointToCoordinate:myOrigin].longitude, [_projection projectedPointToCoordinate:myOrigin].latitude, [_projection projectedPointToCoordinate:topRight].longitude, [_projection projectedPointToCoordinate:topRight].latitude);
[self setProjectedBounds:zoomRect animated:animated];
}
}
... ... @@ -1079,14 +1111,19 @@
if (self.userTrackingMode != RMUserTrackingModeNone)
self.userTrackingMode = RMUserTrackingModeNone;
_userAlteringPanOrZoom = YES;
if (_delegateHasBeforeMapMove)
[_delegate beforeMapMove:self];
[_delegate beforeMapMove:self byUser:_userAlteringPanOrZoom];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate && _delegateHasAfterMapMove)
[_delegate afterMapMove:self];
[_delegate afterMapMove:self byUser:_userAlteringPanOrZoom];
if (!decelerate)
_userAlteringPanOrZoom = NO;
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
... ... @@ -1098,21 +1135,25 @@
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if (_delegateHasAfterMapMove)
[_delegate afterMapMove:self];
[_delegate afterMapMove:self byUser:_userAlteringPanOrZoom];
_userAlteringPanOrZoom = NO;
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
if (_delegateHasAfterMapMove)
[_delegate afterMapMove:self];
[_delegate afterMapMove:self byUser:_userAlteringPanOrZoom];
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
_mapScrollViewIsZooming = YES;
_userAlteringPanOrZoom = (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan);
if (_delegateHasBeforeMapZoom)
[_delegate beforeMapZoom:self];
[_delegate beforeMapZoom:self byUser:_userAlteringPanOrZoom];
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
... ... @@ -1124,7 +1165,9 @@
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if (self.userTrackingMode != RMUserTrackingModeNone && scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateChanged)
_userAlteringPanOrZoom = (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateChanged);
if (self.userTrackingMode != RMUserTrackingModeNone && _userAlteringPanOrZoom)
self.userTrackingMode = RMUserTrackingModeNone;
[self correctPositionOfAllAnnotations];
... ... @@ -1133,7 +1176,7 @@
self.userTrackingMode = RMUserTrackingModeFollow;
if (_delegateHasAfterMapZoom)
[_delegate afterMapZoom:self];
[_delegate afterMapZoom:self byUser:_userAlteringPanOrZoom];
}
// Detect dragging/zooming
... ... @@ -1272,7 +1315,7 @@
// Don't do anything stupid here or your scrolling experience will suck
if (_delegateHasMapViewRegionDidChange)
[_delegate mapViewRegionDidChange:self];
[_delegate mapViewRegionDidChange:self byUser:_userAlteringPanOrZoom];
}
#pragma mark - Gesture Recognizers and event handling
... ... @@ -1300,7 +1343,7 @@
- (void)handleSingleTap:(UIGestureRecognizer *)recognizer
{
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:_overlayView]];
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:self]];
if ( ! hit)
{
... ... @@ -1332,9 +1375,17 @@
- (void)doubleTapAtPoint:(CGPoint)aPoint
{
if (self.zoomingInPivotsAroundCenter)
{
[self zoomInToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
}
else if (userTrackingMode != RMUserTrackingModeNone && fabsf(aPoint.x - [self coordinateToPixel:userLocation.location.coordinate].x) < 75 && fabsf(aPoint.y - [self coordinateToPixel:userLocation.location.coordinate].y) < 75)
{
[self zoomInToNextNativeZoomAt:[self coordinateToPixel:userLocation.location.coordinate] animated:YES];
}
else
{
[self zoomInToNextNativeZoomAt:aPoint animated:YES];
}
if (_delegateHasDoubleTapOnMap)
[_delegate doubleTapOnMap:self at:aPoint];
... ... @@ -1342,7 +1393,7 @@
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:_overlayView]];
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:self]];
if ( ! hit)
{
... ... @@ -1373,7 +1424,12 @@
- (void)handleTwoFingerSingleTap:(UIGestureRecognizer *)recognizer
{
[self zoomOutToNextNativeZoomAt:[self convertPoint:self.center fromView:self.superview] animated:YES];
CGPoint centerPoint = [self convertPoint:self.center fromView:self.superview];
if (userTrackingMode != RMUserTrackingModeNone)
centerPoint = [self coordinateToPixel:userLocation.location.coordinate];
[self zoomOutToNextNativeZoomAt:centerPoint animated:YES];
if (_delegateHasSingleTapTwoFingersOnMap)
[_delegate singleTapTwoFingersOnMap:self at:[recognizer locationInView:self]];
... ... @@ -1396,6 +1452,9 @@
// check whether our custom pan gesture recognizer should start recognizing the gesture
CALayer *hit = [_overlayView.layer hitTest:[recognizer locationInView:_overlayView]];
if ([hit isEqual:_overlayView.layer])
return NO;
if (!hit || ([hit respondsToSelector:@selector(enableDragging)] && ![(RMMarker *)hit enableDragging]))
return NO;
... ... @@ -2241,6 +2300,8 @@
if (annotation.layer == nil)
continue;
annotation.layer.transform = _annotationTransform;
// Use the zPosition property to order the layer hierarchy
if ( ! [_visibleAnnotations containsObject:annotation])
{
... ... @@ -2292,6 +2353,8 @@
if (annotation.layer == nil)
continue;
annotation.layer.transform = _annotationTransform;
if (![_visibleAnnotations containsObject:annotation])
{
if (!lastLayer)
... ... @@ -2449,7 +2512,6 @@
self.userLocation = [RMUserLocation annotationWithMapView:self coordinate:CLLocationCoordinate2DMake(0, 0) andTitle:nil];
locationManager = [[CLLocationManager alloc] init];
locationManager.headingFilter = 5;
locationManager.delegate = self;
[locationManager startUpdatingLocation];
}
... ... @@ -2469,11 +2531,15 @@
NSMutableArray *annotationsToRemove = [NSMutableArray array];
for (RMAnnotation *annotation in _annotations)
{
if (annotation.isUserLocationAnnotation)
[annotationsToRemove addObject:annotation];
}
for (RMAnnotation *annotationToRemove in annotationsToRemove)
{
[self removeAnnotation:annotationToRemove];
}
self.userLocation = nil;
}
... ... @@ -2524,20 +2590,27 @@
{
[locationManager stopUpdatingHeading];
[CATransaction setAnimationDuration:0.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[UIView animateWithDuration:(animated ? 0.5 : 0.0)
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
animations:^(void)
{
_mapScrollView.transform = CGAffineTransformIdentity;
_overlayView.transform = CGAffineTransformIdentity;
_mapTransform = CGAffineTransformIdentity;
_annotationTransform = CATransform3DIdentity;
_mapScrollView.transform = _mapTransform;
_overlayView.transform = _mapTransform;
for (RMAnnotation *annotation in _annotations)
if ( ! annotation.isUserLocationAnnotation)
annotation.layer.transform = CATransform3DIdentity;
annotation.layer.transform = _annotationTransform;
}
completion:nil];
[CATransaction commit];
if (userLocationTrackingView || userHeadingTrackingView)
{
[userLocationTrackingView removeFromSuperview];
... ... @@ -2567,20 +2640,27 @@
userHeadingTrackingView = nil;
}
[CATransaction setAnimationDuration:0.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[UIView animateWithDuration:(animated ? 0.5 : 0.0)
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
animations:^(void)
{
_mapScrollView.transform = CGAffineTransformIdentity;
_overlayView.transform = CGAffineTransformIdentity;
_mapTransform = CGAffineTransformIdentity;
_annotationTransform = CATransform3DIdentity;
_mapScrollView.transform = _mapTransform;
_overlayView.transform = _mapTransform;
for (RMAnnotation *annotation in _annotations)
if ( ! annotation.isUserLocationAnnotation)
annotation.layer.transform = CATransform3DIdentity;
annotation.layer.transform = _annotationTransform;
}
completion:nil];
[CATransaction commit];
userLocation.layer.hidden = NO;
break;
... ... @@ -2593,20 +2673,32 @@
userHeadingTrackingView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"HeadingAngleSmall.png"]];
userHeadingTrackingView.center = CGPointMake(round([self bounds].size.width / 2),
round([self bounds].size.height / 2) - (userHeadingTrackingView.bounds.size.height / 2) - 4);
userHeadingTrackingView.frame = CGRectMake((self.bounds.size.width / 2) - (userHeadingTrackingView.bounds.size.width / 2),
(self.bounds.size.height / 2) - userHeadingTrackingView.bounds.size.height,
userHeadingTrackingView.bounds.size.width,
userHeadingTrackingView.bounds.size.height * 2);
userHeadingTrackingView.contentMode = UIViewContentModeTop;
userHeadingTrackingView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
userHeadingTrackingView.alpha = 0.0;
[self addSubview:userHeadingTrackingView];
[UIView animateWithDuration:0.5 animations:^(void) { userHeadingTrackingView.alpha = 1.0; }];
userLocationTrackingView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"TrackingDot.png"]];
userLocationTrackingView.center = CGPointMake(round([self bounds].size.width / 2),
round([self bounds].size.height / 2));
userLocationTrackingView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
[self addSubview:userLocationTrackingView];
if (self.zoom < 3)
... ... @@ -2615,6 +2707,8 @@
if (self.userLocation)
[self locationManager:locationManager didUpdateToLocation:self.userLocation.location fromLocation:self.userLocation.location];
[self updateHeadingForDeviceOrientation];
[locationManager startUpdatingHeading];
break;
... ... @@ -2640,45 +2734,61 @@
if (self.userTrackingMode != RMUserTrackingModeNone)
{
// zoom centered on user location unless we're already centered there (or very close)
// center on user location unless we're already centered there (or very close)
//
CGPoint mapCenterPoint = [self convertPoint:self.center fromView:self.superview];
CGPoint userLocationPoint = [self mapPositionForAnnotation:userLocation];
if (fabsf(userLocationPoint.x - mapCenterPoint.x) > 2 || fabsf(userLocationPoint.y - mapCenterPoint.y > 2))
if (fabsf(userLocationPoint.x - mapCenterPoint.x) || fabsf(userLocationPoint.y - mapCenterPoint.y))
{
if (round(_zoom) >= 10)
{
// at sufficient detail, just re-center the map; don't zoom
//
[self setCenterCoordinate:userLocation.location.coordinate animated:YES];
}
else
{
float delta = newLocation.horizontalAccuracy / 110000; // approx. meter per degree latitude
// otherwise re-center and zoom in to near accuracy confidence
//
float delta = (newLocation.horizontalAccuracy / 110000) * 1.2; // approx. meter per degree latitude, plus some margin
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(newLocation.coordinate.latitude - delta,
CLLocationCoordinate2D desiredSouthWest = CLLocationCoordinate2DMake(newLocation.coordinate.latitude - delta,
newLocation.coordinate.longitude - delta);
CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(newLocation.coordinate.latitude + delta,
CLLocationCoordinate2D desiredNorthEast = CLLocationCoordinate2DMake(newLocation.coordinate.latitude + delta,
newLocation.coordinate.longitude + delta);
if (northEast.latitude != [self latitudeLongitudeBoundingBox].northEast.latitude ||
northEast.longitude != [self latitudeLongitudeBoundingBox].northEast.longitude ||
southWest.latitude != [self latitudeLongitudeBoundingBox].southWest.latitude ||
southWest.longitude != [self latitudeLongitudeBoundingBox].southWest.longitude)
[self zoomWithLatitudeLongitudeBoundsSouthWest:southWest northEast:northEast animated:YES];
CGFloat pixelRadius = fminf(self.bounds.size.width, self.bounds.size.height) / 2;
CLLocationCoordinate2D actualSouthWest = [self pixelToCoordinate:CGPointMake(userLocationPoint.x - pixelRadius, userLocationPoint.y - pixelRadius)];
CLLocationCoordinate2D actualNorthEast = [self pixelToCoordinate:CGPointMake(userLocationPoint.x + pixelRadius, userLocationPoint.y + pixelRadius)];
if (desiredNorthEast.latitude != actualNorthEast.latitude ||
desiredNorthEast.longitude != actualNorthEast.longitude ||
desiredSouthWest.latitude != actualSouthWest.latitude ||
desiredSouthWest.longitude != actualSouthWest.longitude)
{
[self zoomWithLatitudeLongitudeBoundsSouthWest:desiredSouthWest northEast:desiredNorthEast animated:YES];
}
}
}
}
RMAnnotation *accuracyCircleAnnotation = nil;
for (RMAnnotation *annotation in _annotations)
{
if ([annotation.annotationType isEqualToString:kRMAccuracyCircleAnnotationTypeName])
accuracyCircleAnnotation = annotation;
}
if ( ! accuracyCircleAnnotation)
{
accuracyCircleAnnotation = [RMAnnotation annotationWithMapView:self coordinate:newLocation.coordinate andTitle:nil];
accuracyCircleAnnotation.annotationType = kRMAccuracyCircleAnnotationTypeName;
accuracyCircleAnnotation.clusteringEnabled = NO;
accuracyCircleAnnotation.layer = [[RMCircle alloc] initWithView:self radiusInMeters:newLocation.horizontalAccuracy];
accuracyCircleAnnotation.isUserLocationAnnotation = YES;
((RMCircle *)accuracyCircleAnnotation.layer).lineColor = [UIColor colorWithRed:0.378 green:0.552 blue:0.827 alpha:0.7];
... ... @@ -2698,21 +2808,20 @@
RMAnnotation *trackingHaloAnnotation = nil;
for (RMAnnotation *annotation in _annotations)
{
if ([annotation.annotationType isEqualToString:kRMTrackingHaloAnnotationTypeName])
trackingHaloAnnotation = annotation;
}
if ( ! trackingHaloAnnotation)
{
trackingHaloAnnotation = [RMAnnotation annotationWithMapView:self coordinate:newLocation.coordinate andTitle:nil];
trackingHaloAnnotation.annotationType = kRMTrackingHaloAnnotationTypeName;
trackingHaloAnnotation.clusteringEnabled = NO;
// create image marker
//
trackingHaloAnnotation.layer = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:@"TrackingDotHalo.png"]];
trackingHaloAnnotation.isUserLocationAnnotation = YES;
[CATransaction begin];
... ... @@ -2722,14 +2831,10 @@
// scale out radially
//
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
boundsAnimation.repeatCount = MAXFLOAT;
boundsAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
boundsAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
boundsAnimation.removedOnCompletion = NO;
boundsAnimation.fillMode = kCAFillModeForwards;
[trackingHaloAnnotation.layer addAnimation:boundsAnimation forKey:@"animateScale"];
... ... @@ -2737,14 +2842,10 @@
// go transparent as scaled out
//
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.repeatCount = MAXFLOAT;
opacityAnimation.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnimation.toValue = [NSNumber numberWithFloat:-1.0];
opacityAnimation.removedOnCompletion = NO;
opacityAnimation.fillMode = kCAFillModeForwards;
[trackingHaloAnnotation.layer addAnimation:opacityAnimation forKey:@"animateOpacity"];
... ... @@ -2765,16 +2866,18 @@
if ( ! [_annotations containsObject:userLocation])
[self addAnnotation:userLocation];
[self correctPositionOfAllAnnotations];
}
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
return YES;
return self.displayHeadingCalibration;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
if ( ! showsUserLocation || _mapScrollView.isDragging)
if ( ! showsUserLocation || _mapScrollView.isDragging || newHeading.headingAccuracy < 0)
return;
userLocation.heading = newHeading;
... ... @@ -2784,23 +2887,29 @@
if (newHeading.trueHeading != 0 && self.userTrackingMode == RMUserTrackingModeFollowWithHeading)
{
if (userHeadingTrackingView.alpha < 1.0)
[UIView animateWithDuration:0.5 animations:^(void) { userHeadingTrackingView.alpha = 1.0; }];
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationDuration:0.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[UIView animateWithDuration:1.0
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
animations:^(void)
{
CGFloat angle = (M_PI / -180) * newHeading.trueHeading;
_mapScrollView.transform = CGAffineTransformMakeRotation(angle);
_overlayView.transform = CGAffineTransformMakeRotation(angle);
_mapTransform = CGAffineTransformMakeRotation(angle);
_annotationTransform = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(-angle));
_mapScrollView.transform = _mapTransform;
_overlayView.transform = _mapTransform;
for (RMAnnotation *annotation in _annotations)
if ( ! annotation.isUserLocationAnnotation)
annotation.layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(-angle));
if ([annotation.layer isKindOfClass:[RMMarker class]] && ! annotation.isUserLocationAnnotation)
annotation.layer.transform = _annotationTransform;
}
completion:nil];
... ... @@ -2810,12 +2919,42 @@
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if ([error code] != kCLErrorLocationUnknown)
{
self.userTrackingMode = RMUserTrackingModeNone;
if (_delegateHasDidFailToLocateUserWithError)
[_delegate mapView:self didFailToLocateUserWithError:error];
}
- (void)updateHeadingForDeviceOrientation
{
if (locationManager)
{
// note that right/left device and interface orientations are opposites (see UIApplication.h)
//
switch ([[UIApplication sharedApplication] statusBarOrientation])
{
case (UIInterfaceOrientationLandscapeLeft):
{
locationManager.headingOrientation = CLDeviceOrientationLandscapeRight;
break;
}
case (UIInterfaceOrientationLandscapeRight):
{
locationManager.headingOrientation = CLDeviceOrientationLandscapeLeft;
break;
}
case (UIInterfaceOrientationPortraitUpsideDown):
{
locationManager.headingOrientation = CLDeviceOrientationPortraitUpsideDown;
break;
}
case (UIInterfaceOrientationPortrait):
default:
{
locationManager.headingOrientation = CLDeviceOrientationPortrait;
break;
}
}
}
}
... ... @@ -2824,39 +2963,39 @@
- (UIViewController *)viewControllerPresentingAttribution
{
return viewControllerPresentingAttribution;
return _viewControllerPresentingAttribution;
}
- (void)setViewControllerPresentingAttribution:(UIViewController *)viewController
{
viewControllerPresentingAttribution = viewController;
_viewControllerPresentingAttribution = viewController;
if (self.viewControllerPresentingAttribution && ! attributionButton)
if (_viewControllerPresentingAttribution && ! _attributionButton)
{
attributionButton = [[UIButton buttonWithType:UIButtonTypeInfoLight] retain];
_attributionButton = [[UIButton buttonWithType:UIButtonTypeInfoLight] retain];
attributionButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
_attributionButton.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
[attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside];
[_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchDown];
attributionButton.frame = CGRectMake(self.bounds.size.width - 30,
_attributionButton.frame = CGRectMake(self.bounds.size.width - 30,
self.bounds.size.height - 30,
attributionButton.bounds.size.width,
attributionButton.bounds.size.height);
_attributionButton.bounds.size.width,
_attributionButton.bounds.size.height);
[self addSubview:attributionButton];
[self addSubview:_attributionButton];
}
}
- (void)showAttribution:(id)sender
{
if (self.viewControllerPresentingAttribution)
if (_viewControllerPresentingAttribution)
{
RMAttributionViewController *attributionViewController = [[[RMAttributionViewController alloc] initWithMapView:self] autorelease];
attributionViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self.viewControllerPresentingAttribution presentModalViewController:attributionViewController animated:YES];
[_viewControllerPresentingAttribution presentModalViewController:attributionViewController animated:YES];
}
}
... ...
... ... @@ -31,7 +31,7 @@
@class RMAnnotation;
@class RMUserLocation;
typedef enum {
typedef enum : NSUInteger {
RMUserTrackingModeNone = 0,
RMUserTrackingModeFollow = 1,
RMUserTrackingModeFollowWithHeading = 2
... ... @@ -45,11 +45,11 @@ typedef enum {
- (void)mapView:(RMMapView *)mapView willHideLayerForAnnotation:(RMAnnotation *)annotation;
- (void)mapView:(RMMapView *)mapView didHideLayerForAnnotation:(RMAnnotation *)annotation;
- (void)beforeMapMove:(RMMapView *)map;
- (void)afterMapMove:(RMMapView *)map;
- (void)beforeMapMove:(RMMapView *)map byUser:(BOOL)wasUserAction;
- (void)afterMapMove:(RMMapView *)map byUser:(BOOL)wasUserAction;
- (void)beforeMapZoom:(RMMapView *)map;
- (void)afterMapZoom:(RMMapView *)map;
- (void)beforeMapZoom:(RMMapView *)map byUser:(BOOL)wasUserAction;
- (void)afterMapZoom:(RMMapView *)map byUser:(BOOL)wasUserAction;
/*
\brief Tells the delegate that the region displayed by the map view just changed.
... ... @@ -57,7 +57,7 @@ typedef enum {
During scrolling and zooming, this method may be called many times to report updates to the map position.
Therefore, your implementation of this method should be as lightweight as possible to avoid affecting scrolling and zooming performance.
*/
- (void)mapViewRegionDidChange:(RMMapView *)mapView;
- (void)mapViewRegionDidChange:(RMMapView *)mapView byUser:(BOOL)wasUserAction;
- (void)doubleTapOnMap:(RMMapView *)map at:(CGPoint)point;
- (void)singleTapOnMap:(RMMapView *)map at:(CGPoint)point;
... ...
... ... @@ -135,8 +135,8 @@
NSURL *imageURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://a.tiles.mapbox.com/v3/marker/pin-%@%@%@%@.png",
(sizeString ? [sizeString substringToIndex:1] : @"m"),
(symbolName ? [@"-" stringByAppendingString:symbolName] : nil),
(colorHex ? [@"+" stringByAppendingString:[colorHex stringByReplacingOccurrencesOfString:@"#" withString:@""]] : nil),
(symbolName ? [@"-" stringByAppendingString:symbolName] : @""),
(colorHex ? [@"+" stringByAppendingString:[colorHex stringByReplacingOccurrencesOfString:@"#" withString:@""]] : @""),
(useRetina ? @"@2x" : @"")]];
UIImage *image;
... ...
... ... @@ -25,7 +25,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <sys/sysctl.h>
#import <sys/utsname.h>
#import "RMTileCache.h"
#import "RMMemoryCache.h"
... ... @@ -213,25 +213,19 @@
@implementation RMTileCache (Configuration)
+ (NSString *)sysctlbyname:(NSString *)name
{
size_t len;
sysctlbyname([name UTF8String], NULL, &len, NULL, 0);
char *sysctlResult = malloc(len);
sysctlbyname([name UTF8String], sysctlResult, &len, NULL, 0);
NSString *result = [NSString stringWithCString:sysctlResult encoding:NSASCIIStringEncoding];
free(sysctlResult);
return result;
}
static NSMutableDictionary *predicateValues = nil;
- (NSDictionary *)predicateValues
{
NSString *machine = [RMTileCache sysctlbyname:@"hw.machine"];
static dispatch_once_t predicateValuesOnceToken;
dispatch_once(&predicateValuesOnceToken, ^{
struct utsname systemInfo;
uname(&systemInfo);
NSMutableDictionary *predicateValues = [NSMutableDictionary dictionaryWithObjectsAndKeys:
NSString *machine = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding];
predicateValues = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
[[UIDevice currentDevice] model], @"model",
machine, @"machine",
[[UIDevice currentDevice] systemName], @"systemName",
... ... @@ -258,6 +252,7 @@
// See NSPredicate
// NSLog(@"Predicate values:\n%@", [predicateValues description]);
});
return predicateValues;
}
... ...
... ... @@ -124,6 +124,9 @@
- (BOOL)addTileSource:(id<RMTileSource>)tileSource atIndex:(NSUInteger)index
{
if ( ! tileSource)
return NO;
[_tileSourcesLock lock];
RMProjection *newProjection = [tileSource projection];
... ...
... ... @@ -11,7 +11,7 @@
@interface RMUserLocation : RMAnnotation
@property (nonatomic, readonly, getter=isUpdating) BOOL updating;
@property (nonatomic, readonly, retain) CLLocation *location;
@property (nonatomic, readonly, retain) CLHeading *heading;
@property (nonatomic, readonly) CLLocation *location;
@property (nonatomic, readonly) CLHeading *heading;
@end
... ...
... ... @@ -22,7 +22,7 @@ typedef enum {
@property (nonatomic, retain) UISegmentedControl *segmentedControl;
@property (nonatomic, retain) UIImageView *buttonImageView;
@property (nonatomic, retain) UIActivityIndicatorView *activityView;
@property (nonatomic) RMUserTrackingButtonState state;
@property (nonatomic, assign) RMUserTrackingButtonState state;
- (void)updateAppearance;
- (void)changeMode:(id)sender;
... ... @@ -33,7 +33,7 @@ typedef enum {
@implementation RMUserTrackingBarButtonItem
@synthesize mapView=_mapView;
@synthesize mapView = _mapView;
@synthesize segmentedControl;
@synthesize buttonImageView;
@synthesize activityView;
... ... @@ -41,7 +41,7 @@ typedef enum {
- (id)initWithMapView:(RMMapView *)mapView
{
if (!(self = [super initWithCustomView:[[UIControl alloc] initWithFrame:CGRectMake(0, 0, 32, 32)]]))
if ( ! (self = [super initWithCustomView:[[UIControl alloc] initWithFrame:CGRectMake(0, 0, 32, 32)]]))
return nil;
segmentedControl = [[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@""]] retain];
... ...
... ... @@ -107,7 +107,8 @@
DD8CDB4B14E0507100B73EB9 /* RMMapQuestOSMSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8CDB4914E0507100B73EB9 /* RMMapQuestOSMSource.m */; };
DD8FD7541559E4A40044D96F /* RMUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DD8FD7521559E4A40044D96F /* RMUserLocation.h */; };
DD8FD7551559E4A40044D96F /* RMUserLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = DD8FD7531559E4A40044D96F /* RMUserLocation.m */; };
DD96559215264C810008517A /* RMMapBoxSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DD96559015264C810008517A /* RMMapBoxSource.h */; };
DD98B6FA14D76B930092882F /* RMMapBoxSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DD98B6F814D76B930092882F /* RMMapBoxSource.h */; };
DD98B6FB14D76B930092882F /* RMMapBoxSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DD98B6F914D76B930092882F /* RMMapBoxSource.m */; };
DDA6B8BD155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.h in Headers */ = {isa = PBXBuildFile; fileRef = DDA6B8BB155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.h */; };
DDA6B8BE155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = DDA6B8BC155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.m */; };
DDC4BED5152E3BD700089409 /* RMInteractiveSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DDC4BED3152E3BD700089409 /* RMInteractiveSource.h */; };
... ... @@ -230,8 +231,6 @@
DD5A200815CAD03700FE4157 /* libGRMustache4-iOS.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libGRMustache4-iOS.a"; path = "GRMustache/lib/libGRMustache4-iOS.a"; sourceTree = "<group>"; };
DD5A200A15CAD09400FE4157 /* GRMustache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GRMustache.h; path = GRMustache/include/GRMustache.h; sourceTree = "<group>"; };
DD624B4315B4EB53004524C4 /* loading.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = loading.png; path = Resources/loading.png; sourceTree = "<group>"; };
DD6380DB152E72880074E66E /* RMMapBoxSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapBoxSource.h; sourceTree = "<group>"; };
DD6380DC152E72880074E66E /* RMMapBoxSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMapBoxSource.m; sourceTree = "<group>"; };
DD8CDB4814E0507100B73EB9 /* RMMapQuestOSMSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapQuestOSMSource.h; sourceTree = "<group>"; };
DD8CDB4914E0507100B73EB9 /* RMMapQuestOSMSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMapQuestOSMSource.m; sourceTree = "<group>"; };
DD8FD7521559E4A40044D96F /* RMUserLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMUserLocation.h; sourceTree = "<group>"; };
... ... @@ -242,7 +241,8 @@
DD8FD7661559EE120044D96F /* TrackingDot@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "TrackingDot@2x.png"; path = "Resources/TrackingDot@2x.png"; sourceTree = "<group>"; };
DD8FD7691559EE120044D96F /* TrackingDotHalo@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "TrackingDotHalo@2x.png"; path = "Resources/TrackingDotHalo@2x.png"; sourceTree = "<group>"; };
DD8FD76C1559EE120044D96F /* TrackingDotHalo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = TrackingDotHalo.png; path = Resources/TrackingDotHalo.png; sourceTree = "<group>"; };
DD96559015264C810008517A /* RMMapBoxSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapBoxSource.h; sourceTree = "<group>"; };
DD98B6F814D76B930092882F /* RMMapBoxSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapBoxSource.h; sourceTree = "<group>"; };
DD98B6F914D76B930092882F /* RMMapBoxSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMapBoxSource.m; sourceTree = "<group>"; };
DDA6B8BB155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMUserTrackingBarButtonItem.h; sourceTree = "<group>"; };
DDA6B8BC155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMUserTrackingBarButtonItem.m; sourceTree = "<group>"; };
DDA6B8C0155CAB9A003DB5D8 /* TrackingLocation.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = TrackingLocation.png; path = Resources/TrackingLocation.png; sourceTree = "<group>"; };
... ... @@ -290,6 +290,8 @@
D1437B32122869E400888DAE /* RMDBMapSource.m */,
1607499314E120A100D535F5 /* RMGenericMapSource.h */,
1607499414E120A100D535F5 /* RMGenericMapSource.m */,
DD98B6F814D76B930092882F /* RMMapBoxSource.h */,
DD98B6F914D76B930092882F /* RMMapBoxSource.m */,
16FFF2C914E3DBF700A170EC /* RMMapQuestOpenAerialSource.h */,
16FFF2CA14E3DBF700A170EC /* RMMapQuestOpenAerialSource.m */,
DD8CDB4814E0507100B73EB9 /* RMMapQuestOSMSource.h */,
... ... @@ -304,10 +306,6 @@
161E56391594664E00B00BB6 /* RMOpenSeaMapLayer.m */,
B83E64ED0E80E73F001663B6 /* RMOpenStreetMapSource.h */,
B83E64EE0E80E73F001663B6 /* RMOpenStreetMapSource.m */,
DD2B375314CF8197008DE8CB /* RMMBTilesSource.h */,
DD2B375414CF8197008DE8CB /* RMMBTilesSource.m */,
DD6380DB152E72880074E66E /* RMMapBoxSource.h */,
DD6380DC152E72880074E66E /* RMMapBoxSource.m */,
);
name = "Map sources";
sourceTree = "<group>";
... ... @@ -503,6 +501,7 @@
DD8FD7531559E4A40044D96F /* RMUserLocation.m */,
DDA6B8BB155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.h */,
DDA6B8BC155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.m */,
DD8FD7581559EDA80044D96F /* Resources */,
);
name = "User Location";
sourceTree = "<group>";
... ... @@ -588,15 +587,15 @@
DD2B374F14CF814F008DE8CB /* FMDatabasePool.h in Headers */,
DD2B375114CF814F008DE8CB /* FMDatabaseQueue.h in Headers */,
DD2B375514CF8197008DE8CB /* RMMBTilesSource.h in Headers */,
DD98B6FA14D76B930092882F /* RMMapBoxSource.h in Headers */,
DD8CDB4A14E0507100B73EB9 /* RMMapQuestOSMSource.h in Headers */,
1607499514E120A100D535F5 /* RMGenericMapSource.h in Headers */,
16FFF2CB14E3DBF700A170EC /* RMMapQuestOpenAerialSource.h in Headers */,
DD96559215264C810008517A /* RMMapBoxSource.h in Headers */,
DDC4BED5152E3BD700089409 /* RMInteractiveSource.h in Headers */,
DD6380DD152E72880074E66E /* RMMapBoxSource.h in Headers */,
DD3BEF7915913C55007892D8 /* RMAttributionViewController.h in Headers */,
DD8FD7541559E4A40044D96F /* RMUserLocation.h in Headers */,
DDA6B8BD155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.h in Headers */,
DD3BEF7915913C55007892D8 /* RMAttributionViewController.h in Headers */,
16F3581B15864135003A3AD9 /* RMMapScrollView.h in Headers */,
16F98C961590CFF000FF90CE /* RMShape.h in Headers */,
16FBF07615936BF1004ECAD1 /* RMTileSourcesContainer.h in Headers */,
... ... @@ -707,14 +706,14 @@
DD2B375014CF814F008DE8CB /* FMDatabasePool.m in Sources */,
DD2B375214CF814F008DE8CB /* FMDatabaseQueue.m in Sources */,
DD2B375614CF8197008DE8CB /* RMMBTilesSource.m in Sources */,
DD98B6FB14D76B930092882F /* RMMapBoxSource.m in Sources */,
DD8CDB4B14E0507100B73EB9 /* RMMapQuestOSMSource.m in Sources */,
1607499614E120A100D535F5 /* RMGenericMapSource.m in Sources */,
16FFF2CC14E3DBF700A170EC /* RMMapQuestOpenAerialSource.m in Sources */,
DD6380DE152E72880074E66E /* RMMapBoxSource.m in Sources */,
DDC4BEF2152E3FAE00089409 /* RMInteractiveSource.m in Sources */,
DD3BEF7A15913C55007892D8 /* RMAttributionViewController.m in Sources */,
DD8FD7551559E4A40044D96F /* RMUserLocation.m in Sources */,
DDA6B8BE155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.m in Sources */,
DD3BEF7A15913C55007892D8 /* RMAttributionViewController.m in Sources */,
16F3581C15864135003A3AD9 /* RMMapScrollView.m in Sources */,
16F98C971590CFF000FF90CE /* RMShape.m in Sources */,
16FBF07715936BF1004ECAD1 /* RMTileSourcesContainer.m in Sources */,
... ...
... ... @@ -10,14 +10,16 @@ Undergoing rapid development, so the `develop` branch is currently recommended.
Major differences from [Alpstein fork of Route-Me](https://github.com/Alpstein/route-me):
* Requires iOS 5.0 and above.
* Canonical source for [MapBox](http://mapbox.com) & [MBTiles](http://mbtiles.org) tile source integration code.
* [MapBox](http://mapbox.com) & [MBTiles](http://mbtiles.org) tile source integration code.
* [UTFGrid interactivity](http://mapbox.com/mbtiles-spec/utfgrid/).
* [User location services](http://mapbox.com/blog/ios-user-location-services/).
* Removal of two-finger double-tap gesture for zoom out (to speed up two-finger single-tap recognition like MapKit).
* Different default starting location for maps.
* Built-in attribution view controller with button on map views & default OpenStreetMap attribution.
* [MapBox Markers](http://mapbox.com/blog/markers/) support.
* Prepackaged [binary framework](http://mapbox.com/blog/ios-sdk-framework/).
* Removed of included example projects in favor of separate examples on GitHub.
* A few added defaults for convenience.
* Improved documentation.
[mapbox]: http://mapbox.com
... ...