Authored by Justin R. Miller

merged alpstein/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,66 +76,82 @@
NSArray *URLs = [self URLsForTile:tile];
// fill up collection array with placeholders
//
NSMutableArray *tilesData = [NSMutableArray arrayWithCapacity:[URLs count]];
if ([URLs count] > 1)
{
// fill up collection array with placeholders
//
NSMutableArray *tilesData = [NSMutableArray arrayWithCapacity:[URLs count]];
for (NSUInteger p = 0; p < [URLs count]; ++p)
[tilesData addObject:[NSNull null]];
for (NSUInteger p = 0; p < [URLs count]; ++p)
[tilesData addObject:[NSNull null]];
dispatch_group_t fetchGroup = dispatch_group_create();
dispatch_group_t fetchGroup = dispatch_group_create();
for (NSUInteger u = 0; u < [URLs count]; ++u)
{
NSURL *currentURL = [URLs objectAtIndex:u];
dispatch_group_async(fetchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
for (NSUInteger u = 0; u < [URLs count]; ++u)
{
NSData *tileData = nil;
NSURL *currentURL = [URLs objectAtIndex:u];
for (NSUInteger try = 0; tileData == nil && try < self.retryCount; ++try)
dispatch_group_async(fetchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:currentURL];
[request setTimeoutInterval:(self.waitSeconds / (CGFloat)self.retryCount)];
tileData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
}
NSData *tileData = nil;
for (NSUInteger try = 0; tileData == nil && try < self.retryCount; ++try)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:currentURL];
[request setTimeoutInterval:(self.requestTimeoutSeconds / (CGFloat)self.retryCount)];
tileData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
}
if (tileData)
{
@synchronized(self)
{
// safely put into collection array in proper order
//
[tilesData replaceObjectAtIndex:u withObject:tileData];
};
}
});
}
if (tileData)
// 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.requestTimeoutSeconds));
dispatch_release(fetchGroup);
// composite the collected images together
//
for (NSData *tileData in tilesData)
{
if (tileData && [tileData isKindOfClass:[NSData class]] && [tileData length])
{
@synchronized(self)
if (image != nil)
{
UIGraphicsBeginImageContext(image.size);
[image drawAtPoint:CGPointMake(0,0)];
[[UIImage imageWithData:tileData] drawAtPoint:CGPointMake(0,0)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
// safely put into collection array in proper order
//
[tilesData replaceObjectAtIndex:u withObject:tileData];
};
image = [UIImage imageWithData:tileData];
}
}
});
}
}
// 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_release(fetchGroup);
// composite the collected images together
//
for (NSData *tileData in tilesData)
else
{
if (tileData && [tileData isKindOfClass:[NSData class]] && [tileData length])
for (NSUInteger try = 0; image == nil && try < self.retryCount; ++try)
{
if (image != nil)
{
UIGraphicsBeginImageContext(image.size);
[image drawAtPoint:CGPointMake(0,0)];
[[UIImage imageWithData:tileData] drawAtPoint:CGPointMake(0,0)];
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]];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
image = [UIImage imageWithData:tileData];
}
if (response.statusCode == HTTP_404_NOT_FOUND)
break;
}
}
... ...
... ... @@ -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;
... ...
... ... @@ -88,11 +88,6 @@ 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;
// take missing tiles from lower zoom levels, up to #missingTilesDepth zoom levels (defaults to 0, which disables this feature)
... ... @@ -250,6 +245,11 @@ 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;
- (void)setUserTrackingMode:(RMUserTrackingMode)mode animated:(BOOL)animated;
@end
... ...
... ... @@ -935,9 +935,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];
}
}
... ... @@ -2438,16 +2435,16 @@
{
if (newShowsUserLocation == showsUserLocation)
return;
showsUserLocation = newShowsUserLocation;
if (newShowsUserLocation)
{
if (_delegateHasWillStartLocatingUser)
[_delegate mapViewWillStartLocatingUser:self];
self.userLocation = [RMUserLocation annotationWithMapView:self coordinate:CLLocationCoordinate2DMake(0, 0) andTitle:nil];
locationManager = [[CLLocationManager alloc] init];
locationManager.headingFilter = 5;
locationManager.delegate = self;
... ... @@ -2460,14 +2457,14 @@
locationManager.delegate = nil;
[locationManager release];
locationManager = nil;
if (_delegateHasDidStopLocatingUser)
[_delegate mapViewDidStopLocatingUser:self];
[self setUserTrackingMode:RMUserTrackingModeNone animated:YES];
NSMutableArray *annotationsToRemove = [NSMutableArray array];
for (RMAnnotation *annotation in _annotations)
if (annotation.isUserLocationAnnotation)
[annotationsToRemove addObject:annotation];
... ... @@ -2493,15 +2490,15 @@
if (userLocation)
{
CGPoint locationPoint = [self mapPositionForAnnotation:userLocation];
CGRect locationRect = CGRectMake(locationPoint.x - userLocation.location.horizontalAccuracy,
locationPoint.y - userLocation.location.horizontalAccuracy,
userLocation.location.horizontalAccuracy * 2,
userLocation.location.horizontalAccuracy * 2);
return CGRectIntersectsRect([self bounds], locationRect);
}
return NO;
}
... ... @@ -2514,9 +2511,9 @@
{
if (mode == userTrackingMode)
return;
userTrackingMode = mode;
switch (userTrackingMode)
{
case RMUserTrackingModeNone:
... ... @@ -2533,8 +2530,10 @@
_overlayView.transform = CGAffineTransformIdentity;
for (RMAnnotation *annotation in _annotations)
{
if ( ! annotation.isUserLocationAnnotation)
annotation.layer.transform = CATransform3DIdentity;
}
}
completion:nil];
... ... @@ -2545,15 +2544,15 @@
[userHeadingTrackingView removeFromSuperview];
userHeadingTrackingView = nil;
}
userLocation.layer.hidden = NO;
break;
}
case RMUserTrackingModeFollow:
{
self.showsUserLocation = YES;
[locationManager stopUpdatingHeading];
if (self.userLocation)
... ... @@ -2566,7 +2565,7 @@
[userHeadingTrackingView removeFromSuperview];
userHeadingTrackingView = nil;
}
[UIView animateWithDuration:(animated ? 0.5 : 0.0)
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
... ... @@ -2574,41 +2573,43 @@
{
_mapScrollView.transform = CGAffineTransformIdentity;
_overlayView.transform = CGAffineTransformIdentity;
for (RMAnnotation *annotation in _annotations)
{
if ( ! annotation.isUserLocationAnnotation)
annotation.layer.transform = CATransform3DIdentity;
}
}
completion:nil];
userLocation.layer.hidden = NO;
break;
}
case RMUserTrackingModeFollowWithHeading:
{
self.showsUserLocation = YES;
userLocation.layer.hidden = YES;
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.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));
[self addSubview:userLocationTrackingView];
if (self.zoom < 3)
[self zoomByFactor:exp2f(3 - [self zoom]) near:self.center animated:YES];
... ... @@ -2616,7 +2617,7 @@
[self locationManager:locationManager didUpdateToLocation:self.userLocation.location fromLocation:self.userLocation.location];
[locationManager startUpdatingHeading];
break;
}
}
... ... @@ -2629,7 +2630,7 @@
{
if ( ! showsUserLocation || _mapScrollView.isDragging)
return;
if ([newLocation distanceFromLocation:oldLocation])
{
userLocation.location = newLocation;
... ... @@ -2637,7 +2638,7 @@
if (_delegateHasDidUpdateUserLocation)
[_delegate mapView:self didUpdateUserLocation:userLocation];
}
if (self.userTrackingMode != RMUserTrackingModeNone)
{
// zoom centered on user location unless we're already centered there (or very close)
... ... @@ -2648,7 +2649,7 @@
if (fabsf(userLocationPoint.x - mapCenterPoint.x) > 2 || fabsf(userLocationPoint.y - mapCenterPoint.y > 2))
{
float delta = newLocation.horizontalAccuracy / 110000; // approx. meter per degree latitude
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(newLocation.coordinate.latitude - delta,
newLocation.coordinate.longitude - delta);
... ... @@ -2664,105 +2665,94 @@
}
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];
((RMCircle *)accuracyCircleAnnotation.layer).fillColor = [UIColor colorWithRed:0.378 green:0.552 blue:0.827 alpha:0.15];
((RMCircle *)accuracyCircleAnnotation.layer).lineWidthInPixels = 2.0;
[self addAnnotation:accuracyCircleAnnotation];
}
if ([newLocation distanceFromLocation:oldLocation])
accuracyCircleAnnotation.coordinate = newLocation.coordinate;
if (newLocation.horizontalAccuracy != oldLocation.horizontalAccuracy)
((RMCircle *)accuracyCircleAnnotation.layer).radiusInMeters = newLocation.horizontalAccuracy;
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];
[CATransaction setAnimationDuration:2.5];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
// 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"];
// 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"];
[CATransaction commit];
[self addAnnotation:trackingHaloAnnotation];
}
if ([newLocation distanceFromLocation:oldLocation])
trackingHaloAnnotation.coordinate = newLocation.coordinate;
userLocation.layer.hidden = ((trackingHaloAnnotation.coordinate.latitude == 0 && trackingHaloAnnotation.coordinate.longitude == 0) || self.userTrackingMode == RMUserTrackingModeFollowWithHeading);
accuracyCircleAnnotation.layer.hidden = newLocation.horizontalAccuracy <= 10;
trackingHaloAnnotation.layer.hidden = ((trackingHaloAnnotation.coordinate.latitude == 0 && trackingHaloAnnotation.coordinate.longitude == 0) || newLocation.horizontalAccuracy > 10);
if ( ! [_annotations containsObject:userLocation])
[self addAnnotation:userLocation];
}
... ... @@ -2776,9 +2766,9 @@
{
if ( ! showsUserLocation || _mapScrollView.isDragging)
return;
userLocation.heading = newHeading;
if (_delegateHasDidUpdateUserLocation)
[_delegate mapView:self didUpdateUserLocation:userLocation];
... ... @@ -2787,23 +2777,25 @@
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
animations:^(void)
{
CGFloat angle = (M_PI / -180) * newHeading.trueHeading;
_mapScrollView.transform = CGAffineTransformMakeRotation(angle);
_overlayView.transform = CGAffineTransformMakeRotation(angle);
for (RMAnnotation *annotation in _annotations)
{
if ( ! annotation.isUserLocationAnnotation)
annotation.layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(-angle));
}
}
completion:nil];
[CATransaction commit];
}
}
... ... @@ -2813,7 +2805,7 @@
if ([error code] != kCLErrorLocationUnknown)
{
self.userTrackingMode = RMUserTrackingModeNone;
if (_delegateHasDidFailToLocateUserWithError)
[_delegate mapView:self didFailToLocateUserWithError:error];
}
... ...
... ... @@ -31,7 +31,7 @@
@class RMAnnotation;
@class RMUserLocation;
typedef enum {
typedef enum : NSUInteger {
RMUserTrackingModeNone = 0,
RMUserTrackingModeFollow = 1,
RMUserTrackingModeFollowWithHeading = 2
... ...
... ... @@ -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,51 +213,46 @@
@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;
NSMutableDictionary *predicateValues = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[[UIDevice currentDevice] model], @"model",
machine, @"machine",
[[UIDevice currentDevice] systemName], @"systemName",
[NSNumber numberWithFloat:[[[UIDevice currentDevice] systemVersion] floatValue]], @"systemVersion",
[NSNumber numberWithInt:[[UIDevice currentDevice] userInterfaceIdiom]], @"userInterfaceIdiom",
nil];
dispatch_once(&predicateValuesOnceToken, ^{
struct utsname systemInfo;
uname(&systemInfo);
if ( ! ([machine isEqualToString:@"i386"] || [machine isEqualToString:@"x86_64"]))
{
NSNumber *machineNumber = [NSNumber numberWithFloat:[[[machine stringByTrimmingCharactersInSet:[NSCharacterSet letterCharacterSet]] stringByReplacingOccurrencesOfString:@"," withString:@"."] floatValue]];
NSString *machine = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding];
if ( ! machineNumber)
machineNumber = [NSNumber numberWithFloat:0.0];
predicateValues = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
[[UIDevice currentDevice] model], @"model",
machine, @"machine",
[[UIDevice currentDevice] systemName], @"systemName",
[NSNumber numberWithFloat:[[[UIDevice currentDevice] systemVersion] floatValue]], @"systemVersion",
[NSNumber numberWithInt:[[UIDevice currentDevice] userInterfaceIdiom]], @"userInterfaceIdiom",
nil];
[predicateValues setObject:machineNumber forKey:@"machineNumber"];
}
else
{
[predicateValues setObject:[NSNumber numberWithFloat:0.0] forKey:@"machineNumber"];
}
if ( ! ([machine isEqualToString:@"i386"] || [machine isEqualToString:@"x86_64"]))
{
NSNumber *machineNumber = [NSNumber numberWithFloat:[[[machine stringByTrimmingCharactersInSet:[NSCharacterSet letterCharacterSet]] stringByReplacingOccurrencesOfString:@"," withString:@"."] floatValue]];
// A predicate might be:
// (self.model = 'iPad' and self.machineNumber >= 3) or (self.machine = 'x86_64')
// See NSPredicate
if ( ! machineNumber)
machineNumber = [NSNumber numberWithFloat:0.0];
// NSLog(@"Predicate values:\n%@", [predicateValues description]);
[predicateValues setObject:machineNumber forKey:@"machineNumber"];
}
else
{
[predicateValues setObject:[NSNumber numberWithFloat:0.0] forKey:@"machineNumber"];
}
// A predicate might be:
// (self.model = 'iPad' and self.machineNumber >= 3) or (self.machine = 'x86_64')
// 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
... ...
... ... @@ -26,7 +26,7 @@
layer = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:@"TrackingDot.png"]];
annotationType = [kRMUserLocationAnnotationTypeName retain];
clusteringEnabled = NO;
return self;
... ...
... ... @@ -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,44 +41,44 @@ 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];
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
[segmentedControl setWidth:32.0 forSegmentAtIndex:0];
segmentedControl.userInteractionEnabled = NO;
segmentedControl.tintColor = self.tintColor;
segmentedControl.center = self.customView.center;
[self.customView addSubview:segmentedControl];
buttonImageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"TrackingLocation.png"]] retain];
buttonImageView.contentMode = UIViewContentModeCenter;
buttonImageView.frame = CGRectMake(0, 0, 32, 32);
buttonImageView.center = self.customView.center;
buttonImageView.userInteractionEnabled = NO;
[self.customView addSubview:buttonImageView];
activityView = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] retain];
activityView.hidesWhenStopped = YES;
activityView.center = self.customView.center;
activityView.userInteractionEnabled = NO;
[self.customView addSubview:activityView];
[((UIControl *)self.customView) addTarget:self action:@selector(changeMode:) forControlEvents:UIControlEventTouchUpInside];
_mapView = [mapView retain];
[_mapView addObserver:self forKeyPath:@"userTrackingMode" options:NSKeyValueObservingOptionNew context:nil];
[_mapView addObserver:self forKeyPath:@"userLocation.location" options:NSKeyValueObservingOptionNew context:nil];
state = RMUserTrackingButtonStateLocation;
[self updateAppearance];
return self;
}
... ... @@ -107,7 +107,7 @@ typedef enum {
_mapView = [newMapView retain];
[_mapView addObserver:self forKeyPath:@"userTrackingMode" options:NSKeyValueObservingOptionNew context:nil];
[_mapView addObserver:self forKeyPath:@"userLocation.location" options:NSKeyValueObservingOptionNew context:nil];
[self updateAppearance];
}
}
... ... @@ -115,7 +115,7 @@ typedef enum {
- (void)setTintColor:(UIColor *)newTintColor
{
[super setTintColor:newTintColor];
segmentedControl.tintColor = newTintColor;
}
... ... @@ -133,7 +133,7 @@ typedef enum {
// "selection" state
//
segmentedControl.selectedSegmentIndex = (_mapView.userTrackingMode == RMUserTrackingModeNone ? UISegmentedControlNoSegment : 0);
// activity/image state
//
if (_mapView.userTrackingMode != RMUserTrackingModeNone && ( ! _mapView.userLocation || ! _mapView.userLocation.location || (_mapView.userLocation.location.coordinate.latitude == 0 && _mapView.userLocation.location.coordinate.longitude == 0)))
... ... @@ -151,16 +151,16 @@ typedef enum {
completion:^(BOOL finished)
{
buttonImageView.hidden = YES;
[activityView startAnimating];
[UIView animateWithDuration:0.25 animations:^(void)
{
buttonImageView.transform = CGAffineTransformIdentity;
activityView.transform = CGAffineTransformIdentity;
}];
}];
state = RMUserTrackingButtonStateActivity;
}
else
... ... @@ -182,16 +182,16 @@ typedef enum {
{
buttonImageView.image = [UIImage imageNamed:(_mapView.userTrackingMode == RMUserTrackingModeFollowWithHeading ? @"TrackingHeading.png" : @"TrackingLocation.png")];
buttonImageView.hidden = NO;
[activityView stopAnimating];
[UIView animateWithDuration:0.25 animations:^(void)
{
buttonImageView.transform = CGAffineTransformIdentity;
activityView.transform = CGAffineTransformIdentity;
}];
}];
state = (_mapView.userTrackingMode == RMUserTrackingModeFollowWithHeading ? RMUserTrackingButtonStateHeading : RMUserTrackingButtonStateLocation);
}
}
... ... @@ -216,7 +216,7 @@ typedef enum {
_mapView.userTrackingMode = RMUserTrackingModeFollowWithHeading;
else
_mapView.userTrackingMode = RMUserTrackingModeNone;
break;
}
case RMUserTrackingModeFollowWithHeading:
... ... @@ -227,7 +227,7 @@ typedef enum {
}
}
}
[self updateAppearance];
}
... ...
... ... @@ -100,13 +100,10 @@
DD3BEF7915913C55007892D8 /* RMAttributionViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3BEF7715913C55007892D8 /* RMAttributionViewController.h */; };
DD3BEF7A15913C55007892D8 /* RMAttributionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3BEF7815913C55007892D8 /* RMAttributionViewController.m */; };
DD57D010152F97D000584A7C /* libGRMustache3-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDC4BEE2152E3DE700089409 /* libGRMustache3-iOS.a */; };
DD6380DD152E72880074E66E /* RMMapBoxSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DD6380DB152E72880074E66E /* RMMapBoxSource.h */; };
DD6380DE152E72880074E66E /* RMMapBoxSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DD6380DC152E72880074E66E /* RMMapBoxSource.m */; };
DD8CDB4A14E0507100B73EB9 /* RMMapQuestOSMSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DD8CDB4814E0507100B73EB9 /* RMMapQuestOSMSource.h */; };
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 */; };
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 */; };
... ... @@ -121,6 +118,7 @@
DDC4BEEB152E3DE700089409 /* GRMustacheTemplateRepository.h in Headers */ = {isa = PBXBuildFile; fileRef = DDC4BEE0152E3DE700089409 /* GRMustacheTemplateRepository.h */; };
DDC4BEEC152E3DE700089409 /* GRMustacheVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = DDC4BEE1152E3DE700089409 /* GRMustacheVersion.h */; };
DDC4BEF2152E3FAE00089409 /* RMInteractiveSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DDC4BED4152E3BD700089409 /* RMInteractiveSource.m */; };
DDD6A19015C1BEF200260443 /* RMMapBoxSource.m in Sources */ = {isa = PBXBuildFile; fileRef = DD98B6F914D76B930092882F /* RMMapBoxSource.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
... ... @@ -237,8 +235,6 @@
DD3BEF7715913C55007892D8 /* RMAttributionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMAttributionViewController.h; sourceTree = "<group>"; };
DD3BEF7815913C55007892D8 /* RMAttributionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMAttributionViewController.m; 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>"; };
... ... @@ -249,7 +245,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>"; };
... ... @@ -308,6 +305,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 */,
... ... @@ -322,10 +321,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>";
... ... @@ -521,6 +516,7 @@
DD8FD7531559E4A40044D96F /* RMUserLocation.m */,
DDA6B8BB155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.h */,
DDA6B8BC155CAB67003DB5D8 /* RMUserTrackingBarButtonItem.m */,
DD8FD7581559EDA80044D96F /* Resources */,
);
name = "User Location";
sourceTree = "<group>";
... ... @@ -627,7 +623,6 @@
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 */,
DDC4BEE3152E3DE700089409 /* GRMustache.h in Headers */,
DDC4BEE4152E3DE700089409 /* GRMustacheAvailabilityMacros.h in Headers */,
... ... @@ -639,10 +634,9 @@
DDC4BEEA152E3DE700089409 /* GRMustacheTemplateDelegate.h in Headers */,
DDC4BEEB152E3DE700089409 /* GRMustacheTemplateRepository.h in Headers */,
DDC4BEEC152E3DE700089409 /* GRMustacheVersion.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 */,
... ... @@ -755,16 +749,16 @@
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 */,
161E563B1594664E00B00BB6 /* RMOpenSeaMapLayer.m in Sources */,
1656665615A1DF7900EF3DC7 /* RMCoordinateGridSource.m in Sources */,
DDD6A19015C1BEF200260443 /* RMMapBoxSource.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ...