Authored by Thomas Rasch

o Added the RMShape implementation from @plancalculus which will replace RMPath (addresses #29)

... ... @@ -10,7 +10,7 @@
@class RMMapScrollView;
@protocol UIScrollViewConstraintsDelegate <NSObject>
@protocol RMMapScrollViewDelegate <NSObject>
- (CGPoint)scrollView:(RMMapScrollView *)aScrollView correctedOffsetForContentOffset:(CGPoint)aContentOffset;
- (CGSize)scrollView:(RMMapScrollView *)aScrollView correctedSizeForContentSize:(CGSize)aContentSize;
... ... @@ -19,6 +19,6 @@
@interface RMMapScrollView : UIScrollView
@property (nonatomic, assign) id <UIScrollViewConstraintsDelegate> constraintsDelegate;
@property (nonatomic, assign) id <RMMapScrollViewDelegate> mapScrollViewDelegate;
@end
... ...
... ... @@ -10,28 +10,28 @@
@implementation RMMapScrollView
@synthesize constraintsDelegate;
@synthesize mapScrollViewDelegate;
- (void)setContentOffset:(CGPoint)contentOffset
{
if (self.constraintsDelegate)
contentOffset = [self.constraintsDelegate scrollView:self correctedOffsetForContentOffset:contentOffset];
if (self.mapScrollViewDelegate)
contentOffset = [self.mapScrollViewDelegate scrollView:self correctedOffsetForContentOffset:contentOffset];
[super setContentOffset:contentOffset];
}
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
{
if (self.constraintsDelegate)
contentOffset = [self.constraintsDelegate scrollView:self correctedOffsetForContentOffset:contentOffset];
if (self.mapScrollViewDelegate)
contentOffset = [self.mapScrollViewDelegate scrollView:self correctedOffsetForContentOffset:contentOffset];
[super setContentOffset:contentOffset animated:animated];
}
- (void)setContentSize:(CGSize)contentSize
{
if (self.constraintsDelegate)
contentSize = [self.constraintsDelegate scrollView:self correctedSizeForContentSize:contentSize];
if (self.mapScrollViewDelegate)
contentSize = [self.mapScrollViewDelegate scrollView:self correctedSizeForContentSize:contentSize];
[super setContentSize:contentSize];
}
... ...
... ... @@ -62,10 +62,10 @@ typedef enum {
@protocol RMMercatorToTileProjection;
@protocol RMTileSource;
@protocol UIScrollViewConstraintsDelegate;
@protocol RMMapScrollViewDelegate;
@interface RMMapView : UIView <UIScrollViewDelegate, RMMapOverlayViewDelegate,
RMMapTiledLayerViewDelegate, UIScrollViewConstraintsDelegate>
RMMapTiledLayerViewDelegate, RMMapScrollViewDelegate>
{
id <RMMapViewDelegate> delegate;
... ...
... ... @@ -33,6 +33,7 @@
#import "RMProjection.h"
#import "RMMarker.h"
#import "RMPath.h"
#import "RMShape.h"
#import "RMAnnotation.h"
#import "RMQuadTree.h"
... ... @@ -101,6 +102,7 @@
float _lastZoom;
CGPoint _lastContentOffset, _accumulatedDelta;
CGSize _lastContentSize;
BOOL _mapScrollViewIsZooming;
BOOL _enableDragging, _enableBouncing;
... ... @@ -465,7 +467,7 @@
_constrainMovement = YES;
_constrainingProjectedBounds = RMProjectedRectMake(southWest.x, southWest.y, northEast.x - southWest.x, northEast.y - southWest.y);
mapScrollView.constraintsDelegate = self;
mapScrollView.mapScrollViewDelegate = self;
}
#pragma mark -
... ... @@ -976,10 +978,10 @@
_lastZoom = [self zoom];
_lastContentOffset = mapScrollView.contentOffset;
_accumulatedDelta = CGPointMake(0.0, 0.0);
_lastContentSize = mapScrollView.contentSize;
[mapScrollView addObserver:self forKeyPath:@"contentOffset" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];
if (_constrainMovement)
mapScrollView.constraintsDelegate = self;
mapScrollView.mapScrollViewDelegate = self;
mapScrollView.zoomScale = exp2f([self zoom]);
... ... @@ -1240,8 +1242,27 @@
NSValue *oldValue = [change objectForKey:NSKeyValueChangeOldKey],
*newValue = [change objectForKey:NSKeyValueChangeNewKey];
if (CGPointEqualToPoint([oldValue CGPointValue], [newValue CGPointValue]))
CGPoint oldContentOffset = [oldValue CGPointValue],
newContentOffset = [newValue CGPointValue];
if (CGPointEqualToPoint(oldContentOffset, newContentOffset))
return;
// The first offset during zooming out (animated) is always garbage
if (_mapScrollViewIsZooming == YES &&
mapScrollView.zooming == NO &&
_lastContentSize.width > mapScrollView.contentSize.width &&
(newContentOffset.y - oldContentOffset.y) == 0.0)
{
_lastContentOffset = mapScrollView.contentOffset;
_lastContentSize = mapScrollView.contentSize;
return;
}
// RMLog(@"contentOffset: {%.0f,%.0f} -> {%.1f,%.1f} (%.0f,%.0f)", oldContentOffset.x, oldContentOffset.y, newContentOffset.x, newContentOffset.y, newContentOffset.x - oldContentOffset.x, newContentOffset.y - oldContentOffset.y);
// RMLog(@"contentSize: {%.0f,%.0f} -> {%.0f,%.0f}", _lastContentSize.width, _lastContentSize.height, mapScrollView.contentSize.width, mapScrollView.contentSize.height);
// RMLog(@"isZooming: %d, scrollview.zooming: %d", _mapScrollViewIsZooming, mapScrollView.zooming);
RMProjectedRect planetBounds = projection.planetBounds;
metersPerPixel = planetBounds.size.width / mapScrollView.contentSize.width;
... ... @@ -1274,11 +1295,12 @@
}
else
{
[self correctPositionOfAllAnnotationsIncludingInvisibles:NO animated:YES];
[self correctPositionOfAllAnnotationsIncludingInvisibles:NO animated:(_mapScrollViewIsZooming && !mapScrollView.zooming)];
_lastZoom = zoom;
}
_lastContentOffset = mapScrollView.contentOffset;
_lastContentSize = mapScrollView.contentSize;
// Don't do anything stupid here or your scrolling experience will suck
if (_delegateHasMapViewRegionDidChange)
... ... @@ -1702,9 +1724,9 @@
CGPoint newPosition = CGPointMake((normalizedProjectedPoint.x / metersPerPixel) - mapScrollView.contentOffset.x,
mapScrollView.contentSize.height - (normalizedProjectedPoint.y / metersPerPixel) - mapScrollView.contentOffset.y);
[annotation setPosition:newPosition animated:animated];
// RMLog(@"Change annotation at {%f,%f} in mapView {%f,%f}", annotation.position.x, annotation.position.y, mapScrollView.contentSize.width, mapScrollView.contentSize.height);
[annotation setPosition:newPosition animated:animated];
}
- (void)correctPositionOfAllAnnotationsIncludingInvisibles:(BOOL)correctAllAnnotations animated:(BOOL)animated
... ...
//
// RMShape.h
//
// Copyright (c) 2008-2012, Route-Me Contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#import <UIKit/UIKit.h>
#import "RMFoundation.h"
#import "RMMapLayer.h"
@class RMMapView;
@interface RMShape : RMMapLayer
{
CGRect pathBoundingBox;
/// Width of the line, in pixels
float lineWidth;
// Line dash style
NSArray *lineDashLengths;
CGFloat lineDashPhase;
BOOL scaleLineWidth;
BOOL scaleLineDash; // if YES line dashes will be scaled to keep a constant size if the layer is zoomed
}
- (id)initWithView:(RMMapView *)aMapView;
@property (nonatomic, retain) NSString *fillRule;
@property (nonatomic, retain) NSString *lineCap;
@property (nonatomic, retain) NSString *lineJoin;
@property (nonatomic, retain) UIColor *lineColor;
@property (nonatomic, retain) UIColor *fillColor;
@property (nonatomic, assign) NSArray *lineDashLengths;
@property (nonatomic, assign) CGFloat lineDashPhase;
@property (nonatomic, assign) BOOL scaleLineDash;
@property (nonatomic, assign) float lineWidth;
@property (nonatomic, assign) BOOL scaleLineWidth;
@property (nonatomic, readonly) CGRect pathBoundingBox;
- (void)moveToProjectedPoint:(RMProjectedPoint)projectedPoint;
- (void)moveToScreenPoint:(CGPoint)point;
- (void)moveToCoordinate:(CLLocationCoordinate2D)coordinate;
- (void)addLineToProjectedPoint:(RMProjectedPoint)projectedPoint;
- (void)addLineToScreenPoint:(CGPoint)point;
- (void)addLineToCoordinate:(CLLocationCoordinate2D)coordinate;
// Change the path without recalculating the geometry (performance!)
- (void)performBatchOperations:(void (^)(RMShape *aPath))block;
/// This closes the path, connecting the last point to the first.
/// After this action, no further points can be added to the path.
/// There is no requirement that a path be closed.
- (void)closePath;
@end
... ...
///
// RMShape.m
//
// Copyright (c) 2008-2012, Route-Me Contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#import "RMShape.h"
#import "RMPixel.h"
#import "RMProjection.h"
#import "RMMapView.h"
#import "RMAnnotation.h"
@implementation RMShape
{
BOOL isFirstPoint, ignorePathUpdates;
float lastScale;
CGRect nonClippedBounds;
CGRect previousBounds;
CAShapeLayer *shapeLayer;
UIBezierPath *bezierPath;
RMMapView *mapView;
}
@synthesize scaleLineWidth;
@synthesize lineDashLengths;
@synthesize scaleLineDash;
@synthesize pathBoundingBox;
#define kDefaultLineWidth 2.0
- (id)initWithView:(RMMapView *)aMapView
{
if (!(self = [super init]))
return nil;
mapView = aMapView;
bezierPath = [[UIBezierPath alloc] init];
lineWidth = kDefaultLineWidth;
ignorePathUpdates = NO;
shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.rasterizationScale = [[UIScreen mainScreen] scale];
shapeLayer.lineWidth = lineWidth;
shapeLayer.lineCap = kCALineCapButt;
shapeLayer.lineJoin = kCALineJoinMiter;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
[self addSublayer:shapeLayer];
pathBoundingBox = CGRectZero;
nonClippedBounds = CGRectZero;
previousBounds = CGRectZero;
lastScale = 0.0;
self.masksToBounds = YES;
scaleLineWidth = NO;
scaleLineDash = NO;
isFirstPoint = YES;
[(id)self setValue:[[UIScreen mainScreen] valueForKey:@"scale"] forKey:@"contentsScale"];
return self;
}
- (void)dealloc
{
mapView = nil;
[bezierPath release]; bezierPath = nil;
[shapeLayer release]; shapeLayer = nil;
[super dealloc];
}
- (id <CAAction>)actionForKey:(NSString *)key
{
return nil;
}
#pragma mark -
- (void)recalculateGeometryAnimated:(BOOL)animated
{
if (ignorePathUpdates)
return;
float scale = 1.0f / [mapView metersPerPixel];
// we have to calculate the scaledLineWidth even if scalling did not change
// as the lineWidth might have changed
float scaledLineWidth;
if (scaleLineWidth)
scaledLineWidth = lineWidth * scale;
else
scaledLineWidth = lineWidth;
shapeLayer.lineWidth = scaledLineWidth;
if (lineDashLengths)
{
if (scaleLineDash)
{
NSMutableArray *scaledLineDashLengths = [NSMutableArray array];
for (NSNumber *lineDashLength in lineDashLengths)
{
[scaledLineDashLengths addObject:[NSNumber numberWithFloat:lineDashLength.floatValue * scale]];
}
shapeLayer.lineDashPattern = scaledLineDashLengths;
}
else
{
shapeLayer.lineDashPattern = lineDashLengths;
}
}
// we are about to overwrite nonClippedBounds, therefore we save the old value
CGRect previousNonClippedBounds = nonClippedBounds;
if (scale != lastScale)
{
lastScale = scale;
CGAffineTransform scaling = CGAffineTransformMakeScale(scale, scale);
UIBezierPath *scaledPath = [bezierPath copy];
[scaledPath applyTransform:scaling];
if (animated)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 0;
animation.autoreverses = NO;
animation.fromValue = (id) shapeLayer.path;
animation.toValue = (id) scaledPath.CGPath;
[shapeLayer addAnimation:animation forKey:@"animatePath"];
}
shapeLayer.path = scaledPath.CGPath;
// calculate the bounds of the scaled path
CGRect boundsInMercators = scaledPath.bounds;
nonClippedBounds = CGRectInset(boundsInMercators, -scaledLineWidth, -scaledLineWidth);
[scaledPath release];
}
// if the path is not scaled, nonClippedBounds stay the same as in the previous invokation
// Clip bound rect to screen bounds.
// If bounds are not clipped, they won't display when you zoom in too much.
CGRect screenBounds = [mapView frame];
// we start with the non-clipped bounds and clip them
CGRect clippedBounds = nonClippedBounds;
float offset;
const float outset = 150.0f; // provides a buffer off screen edges for when path is scaled or moved
CGPoint newPosition = self.annotation.position;
// RMLog(@"x:%f y:%f screen bounds: %f %f %f %f", newPosition.x, newPosition.y, screenBounds.origin.x, screenBounds.origin.y, screenBounds.size.width, screenBounds.size.height);
// Clip top
offset = newPosition.y + clippedBounds.origin.y - screenBounds.origin.y + outset;
if (offset < 0.0f)
{
clippedBounds.origin.y -= offset;
clippedBounds.size.height += offset;
}
// Clip left
offset = newPosition.x + clippedBounds.origin.x - screenBounds.origin.x + outset;
if (offset < 0.0f)
{
clippedBounds.origin.x -= offset;
clippedBounds.size.width += offset;
}
// Clip bottom
offset = newPosition.y + clippedBounds.origin.y + clippedBounds.size.height - screenBounds.origin.y - screenBounds.size.height - outset;
if (offset > 0.0f)
{
clippedBounds.size.height -= offset;
}
// Clip right
offset = newPosition.x + clippedBounds.origin.x + clippedBounds.size.width - screenBounds.origin.x - screenBounds.size.width - outset;
if (offset > 0.0f)
{
clippedBounds.size.width -= offset;
}
if (animated)
{
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
positionAnimation.repeatCount = 0;
positionAnimation.autoreverses = NO;
positionAnimation.fromValue = [NSValue valueWithCGPoint:self.position];
positionAnimation.toValue = [NSValue valueWithCGPoint:newPosition];
[self addAnimation:positionAnimation forKey:@"animatePosition"];
}
super.position = newPosition;
// bounds are animated non-clipped but set with clipping
CGPoint previousNonClippedAnchorPoint = CGPointMake(-previousNonClippedBounds.origin.x / previousNonClippedBounds.size.width,
-previousNonClippedBounds.origin.y / previousNonClippedBounds.size.height);
CGPoint nonClippedAnchorPoint = CGPointMake(-nonClippedBounds.origin.x / nonClippedBounds.size.width,
-nonClippedBounds.origin.y / nonClippedBounds.size.height);
CGPoint clippedAnchorPoint = CGPointMake(-clippedBounds.origin.x / clippedBounds.size.width,
-clippedBounds.origin.y / clippedBounds.size.height);
if (animated)
{
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
boundsAnimation.repeatCount = 0;
boundsAnimation.autoreverses = NO;
boundsAnimation.fromValue = [NSValue valueWithCGRect:previousNonClippedBounds];
boundsAnimation.toValue = [NSValue valueWithCGRect:nonClippedBounds];
[self addAnimation:boundsAnimation forKey:@"animateBounds"];
}
self.bounds = clippedBounds;
previousBounds = clippedBounds;
// anchorPoint is animated non-clipped but set with clipping
if (animated)
{
CABasicAnimation *anchorPointAnimation = [CABasicAnimation animationWithKeyPath:@"anchorPoint"];
anchorPointAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anchorPointAnimation.repeatCount = 0;
anchorPointAnimation.autoreverses = NO;
anchorPointAnimation.fromValue = [NSValue valueWithCGPoint:previousNonClippedAnchorPoint];
anchorPointAnimation.toValue = [NSValue valueWithCGPoint:nonClippedAnchorPoint];
[self addAnimation:anchorPointAnimation forKey:@"animateAnchorPoint"];
}
self.anchorPoint = clippedAnchorPoint;
}
#pragma mark -
- (void)addPointToProjectedPoint:(RMProjectedPoint)point withDrawing:(BOOL)isDrawing
{
if (isFirstPoint)
{
isFirstPoint = FALSE;
projectedLocation = point;
self.position = [mapView projectedPointToPixel:projectedLocation];
[bezierPath moveToPoint:CGPointMake(0.0f, 0.0f)];
}
else
{
point.x = point.x - projectedLocation.x;
point.y = point.y - projectedLocation.y;
if (isDrawing)
[bezierPath addLineToPoint:CGPointMake(point.x, -point.y)];
else
[bezierPath moveToPoint:CGPointMake(point.x, -point.y)];
lastScale = 0.0;
[self recalculateGeometryAnimated:NO];
}
[self setNeedsDisplay];
}
- (void)moveToProjectedPoint:(RMProjectedPoint)projectedPoint
{
[self addPointToProjectedPoint:projectedPoint withDrawing:NO];
}
- (void)moveToScreenPoint:(CGPoint)point
{
RMProjectedPoint mercator = [mapView pixelToProjectedPoint:point];
[self moveToProjectedPoint:mercator];
}
- (void)moveToCoordinate:(CLLocationCoordinate2D)coordinate
{
RMProjectedPoint mercator = [[mapView projection] coordinateToProjectedPoint:coordinate];
[self moveToProjectedPoint:mercator];
}
- (void)addLineToProjectedPoint:(RMProjectedPoint)projectedPoint
{
[self addPointToProjectedPoint:projectedPoint withDrawing:YES];
}
- (void)addLineToScreenPoint:(CGPoint)point
{
RMProjectedPoint mercator = [mapView pixelToProjectedPoint:point];
[self addLineToProjectedPoint:mercator];
}
- (void)addLineToCoordinate:(CLLocationCoordinate2D)coordinate
{
RMProjectedPoint mercator = [[mapView projection] coordinateToProjectedPoint:coordinate];
[self addLineToProjectedPoint:mercator];
}
- (void)performBatchOperations:(void (^)(RMShape *aPath))block
{
ignorePathUpdates = YES;
block(self);
ignorePathUpdates = NO;
lastScale = 0.0;
[self recalculateGeometryAnimated:NO];
}
#pragma mark - Accessors
- (void)closePath
{
[bezierPath closePath];
}
- (float)lineWidth
{
return lineWidth;
}
- (void)setLineWidth:(float)newLineWidth
{
lineWidth = newLineWidth;
lastScale = 0.0;
[self recalculateGeometryAnimated:NO];
}
- (NSString *)lineCap
{
return shapeLayer.lineCap;
}
- (void)setLineCap:(NSString *)newLineCap
{
shapeLayer.lineCap = newLineCap;
[self setNeedsDisplay];
}
- (NSString *)lineJoin
{
return shapeLayer.lineJoin;
}
- (void)setLineJoin:(NSString *)newLineJoin
{
shapeLayer.lineJoin = newLineJoin;
[self setNeedsDisplay];
}
- (UIColor *)lineColor
{
return [UIColor colorWithCGColor:shapeLayer.strokeColor];
}
- (void)setLineColor:(UIColor *)aLineColor
{
if (shapeLayer.strokeColor != aLineColor.CGColor)
{
shapeLayer.strokeColor = aLineColor.CGColor;
[self setNeedsDisplay];
}
}
- (UIColor *)fillColor
{
return [UIColor colorWithCGColor:shapeLayer.fillColor];
}
- (void)setFillColor:(UIColor *)aFillColor
{
if (shapeLayer.fillColor != aFillColor.CGColor)
{
shapeLayer.fillColor = aFillColor.CGColor;
[self setNeedsDisplay];
}
}
- (NSString *)fillRule
{
return shapeLayer.fillRule;
}
- (void)setFillRule:(NSString *)fillRule
{
shapeLayer.fillRule = fillRule;
}
- (CGFloat)lineDashPhase
{
return shapeLayer.lineDashPhase;
}
- (void)setLineDashPhase:(CGFloat)dashPhase
{
shapeLayer.lineDashPhase = dashPhase;
}
- (void)setPosition:(CGPoint)newPosition animated:(BOOL)animated
{
if (CGPointEqualToPoint(newPosition, super.position) && CGRectEqualToRect(self.bounds, previousBounds))
return;
[self recalculateGeometryAnimated:animated];
}
@end
... ...
... ... @@ -53,6 +53,8 @@
16EC85D7133CA6C300219947 /* RMCacheObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 16EC85D1133CA6C300219947 /* RMCacheObject.m */; };
16F3581B15864135003A3AD9 /* RMMapScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = 16F3581915864135003A3AD9 /* RMMapScrollView.h */; };
16F3581C15864135003A3AD9 /* RMMapScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 16F3581A15864135003A3AD9 /* RMMapScrollView.m */; };
16F98C961590CFF000FF90CE /* RMShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 16F98C941590CFF000FF90CE /* RMShape.h */; };
16F98C971590CFF000FF90CE /* RMShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 16F98C951590CFF000FF90CE /* RMShape.m */; };
16FAB66413E03D55002F4E1C /* RMQuadTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 16FAB66213E03D55002F4E1C /* RMQuadTree.h */; };
16FAB66513E03D55002F4E1C /* RMQuadTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 16FAB66313E03D55002F4E1C /* RMQuadTree.m */; };
16FFF2CB14E3DBF700A170EC /* RMMapQuestOpenAerialSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 16FFF2C914E3DBF700A170EC /* RMMapQuestOpenAerialSource.h */; };
... ... @@ -204,6 +206,8 @@
16EC85D1133CA6C300219947 /* RMCacheObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMCacheObject.m; sourceTree = "<group>"; };
16F3581915864135003A3AD9 /* RMMapScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapScrollView.h; sourceTree = "<group>"; };
16F3581A15864135003A3AD9 /* RMMapScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMapScrollView.m; sourceTree = "<group>"; };
16F98C941590CFF000FF90CE /* RMShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMShape.h; sourceTree = "<group>"; };
16F98C951590CFF000FF90CE /* RMShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMShape.m; sourceTree = "<group>"; };
16FAB66213E03D55002F4E1C /* RMQuadTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMQuadTree.h; sourceTree = "<group>"; };
16FAB66313E03D55002F4E1C /* RMQuadTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMQuadTree.m; sourceTree = "<group>"; };
16FFF2C914E3DBF700A170EC /* RMMapQuestOpenAerialSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapQuestOpenAerialSource.h; sourceTree = "<group>"; };
... ... @@ -587,6 +591,8 @@
B8F3FC630EA2E792004D8F85 /* RMMarker.m */,
B8CEB1C30ED5A3480014C431 /* RMPath.h */,
B8CEB1C40ED5A3480014C431 /* RMPath.m */,
16F98C941590CFF000FF90CE /* RMShape.h */,
16F98C951590CFF000FF90CE /* RMShape.m */,
25757F4D1291C8640083D504 /* RMCircle.h */,
25757F4E1291C8640083D504 /* RMCircle.m */,
);
... ... @@ -677,6 +683,7 @@
1607499514E120A100D535F5 /* RMGenericMapSource.h in Headers */,
16FFF2CB14E3DBF700A170EC /* RMMapQuestOpenAerialSource.h in Headers */,
16F3581B15864135003A3AD9 /* RMMapScrollView.h in Headers */,
16F98C961590CFF000FF90CE /* RMShape.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -895,6 +902,7 @@
1607499614E120A100D535F5 /* RMGenericMapSource.m in Sources */,
16FFF2CC14E3DBF700A170EC /* RMMapQuestOpenAerialSource.m in Sources */,
16F3581C15864135003A3AD9 /* RMMapScrollView.m in Sources */,
16F98C971590CFF000FF90CE /* RMShape.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ...