-
Notifications
You must be signed in to change notification settings - Fork 2.2k
[ASMapNode] Add custom pin annotation for static maps #1890
Changes from 7 commits
956231b
fc06814
ec6b419
9afabfc
afc9155
5411a6b
c7cd657
f06abf3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,6 +167,14 @@ - (void)setRegion:(MKCoordinateRegion)region | |
self.options = options; | ||
} | ||
|
||
- (void)setMapDelegate:(id<MKMapViewDelegate>)mapDelegate { | ||
_mapDelegate = mapDelegate; | ||
|
||
if (_mapView) { | ||
_mapView.delegate = mapDelegate; | ||
} | ||
} | ||
|
||
#pragma mark - Snapshotter | ||
|
||
- (void)takeSnapshot | ||
|
@@ -209,15 +217,27 @@ - (void)takeSnapshot | |
UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale); | ||
[image drawAtPoint:CGPointZero]; | ||
|
||
// Get a standard annotation view pin. Future implementations should use a custom annotation image property. | ||
MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""]; | ||
UIImage *pinImage = pin.image; | ||
CGSize pinSize = pin.bounds.size; | ||
UIImage *pinImage; | ||
CGPoint pinCenterOffset = CGPointZero; | ||
|
||
// Get a standard annotation view pin if there is no custom annotation block. | ||
if (!strongSelf.imageForStaticMapAnnotationBlock) { | ||
pinImage = [strongSelf.class defaultPinImageWithCenterOffset:&pinCenterOffset]; | ||
} | ||
|
||
for (id<MKAnnotation> annotation in annotations) { | ||
if (strongSelf.imageForStaticMapAnnotationBlock) { | ||
// Get custom annotation view pin from custom annotation block. | ||
pinImage = strongSelf.imageForStaticMapAnnotationBlock(annotation, &pinCenterOffset); | ||
if (!pinImage) { | ||
// just for case block returned nil, which can happen | ||
pinImage = [strongSelf.class defaultPinImageWithCenterOffset:&pinCenterOffset]; | ||
} | ||
} | ||
|
||
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate]; | ||
if (CGRectContainsPoint(finalImageRect, point)) { | ||
CGPoint pinCenterOffset = pin.centerOffset; | ||
CGSize pinSize = pinImage.size; | ||
point.x -= pinSize.width / 2.0; | ||
point.y -= pinSize.height / 2.0; | ||
point.x += pinCenterOffset.x; | ||
|
@@ -235,6 +255,17 @@ - (void)takeSnapshot | |
}]; | ||
} | ||
|
||
+ (UIImage *)defaultPinImageWithCenterOffset:(CGPoint *)centerOffset | ||
{ | ||
static MKAnnotationView *pin; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""]; | ||
}); | ||
*centerOffset = pin.centerOffset; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of instantiating an MKPinAnnotationView every time (which can get very expensive!) let's make this a class method |
||
return pin.image; | ||
} | ||
|
||
- (void)setUpSnapshotter | ||
{ | ||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:self.options]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"info" : { | ||
"version" : 1, | ||
"author" : "xcode" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"images" : [ | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "hill.png", | ||
"scale" : "1x" | ||
}, | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "hill@2x.png", | ||
"scale" : "2x" | ||
}, | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "hill@3x.png", | ||
"scale" : "3x" | ||
} | ||
], | ||
"info" : { | ||
"version" : 1, | ||
"author" : "xcode" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"images" : [ | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "water.png", | ||
"scale" : "1x" | ||
}, | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "water@2x.png", | ||
"scale" : "2x" | ||
}, | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "water@3x.png", | ||
"scale" : "3x" | ||
} | ||
], | ||
"info" : { | ||
"version" : 1, | ||
"author" : "xcode" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// | ||
// CustomMapAnnotation.h | ||
// ASDKMapTest | ||
// | ||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. | ||
// This source code is licensed under the BSD-style license found in the | ||
// LICENSE file in the root directory of this source tree. An additional grant | ||
// of patent rights can be found in the PATENTS file in the same directory. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <MapKit/MapKit.h> | ||
|
||
@interface CustomMapAnnotation : NSObject<MKAnnotation> | ||
|
||
@property (assign, nonatomic) CLLocationCoordinate2D coordinate; | ||
@property (copy, nonatomic, nullable) UIImage *image; | ||
@property (copy, nonatomic, nullable) NSString *title; | ||
@property (copy, nonatomic, nullable) NSString *subtitle; | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// | ||
// CustomMapAnnotation.m | ||
// ASDKMapTest | ||
// | ||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. | ||
// This source code is licensed under the BSD-style license found in the | ||
// LICENSE file in the root directory of this source tree. An additional grant | ||
// of patent rights can be found in the PATENTS file in the same directory. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
// | ||
|
||
#import "CustomMapAnnotation.h" | ||
|
||
@implementation CustomMapAnnotation | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
// | ||
|
||
#import "MapHandlerNode.h" | ||
#import "CustomMapAnnotation.h" | ||
|
||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h> | ||
|
||
|
@@ -90,6 +91,22 @@ - (void)didLoad | |
[_liveMapToggleButton setTitle:[self liveMapStr] withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal]; | ||
[_liveMapToggleButton setTitle:[self liveMapStr] withFont:[UIFont systemFontOfSize:14] withColor:[UIColor blueColor] forState:ASControlStateHighlighted]; | ||
[_liveMapToggleButton addTarget:self action:@selector(toggleLiveMap) forControlEvents:ASControlNodeEventTouchUpInside]; | ||
|
||
// avoiding retain cycles | ||
__weak MapHandlerNode *weakSelf = self; | ||
|
||
self.mapNode.imageForStaticMapAnnotationBlock = ^UIImage *(id<MKAnnotation> annotation, CGPoint *centerOffset){ | ||
MapHandlerNode *grabbedSelf = weakSelf; | ||
if (grabbedSelf) { | ||
if ([annotation isKindOfClass:[CustomMapAnnotation class]]) { | ||
MKAnnotationView *av = [grabbedSelf annotationViewForAnnotation:annotation]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm unfortunately we just cannot allocate views in here, since we're off the main thread. Since CustomMapAnnotation has an image property, let's use that. Also let's make sure to set |
||
return av.image; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't allocate views in this block, since we're off the main thread. @michalziman Since CustomMapAnnotation has an image property, can you modify this like: CustomMapAnnotation *customAnnotation = (CustomMapAnnotation *)annotation;
return customAnnotation.image; |
||
} | ||
} | ||
return nil; | ||
}; | ||
|
||
[self addAnnotations]; | ||
} | ||
|
||
#pragma mark - Layout | ||
|
@@ -183,6 +200,30 @@ -(void)toggleLiveMap | |
|
||
#pragma mark - Helpers | ||
|
||
- (void)addAnnotations { | ||
|
||
MKPointAnnotation *brno = [MKPointAnnotation new]; | ||
brno.coordinate = CLLocationCoordinate2DMake(49.2002211, 16.6078411); | ||
brno.title = @"Brno city"; | ||
|
||
CustomMapAnnotation *atlantic = [CustomMapAnnotation new]; | ||
atlantic.coordinate = CLLocationCoordinate2DMake(38.6442228, -29.9956942); | ||
atlantic.title = @"Atlantic ocean"; | ||
atlantic.image = [UIImage imageNamed:@"Water"]; | ||
|
||
CustomMapAnnotation *kilimanjaro = [CustomMapAnnotation new]; | ||
kilimanjaro.coordinate = CLLocationCoordinate2DMake(-3.075833, 37.353333); | ||
kilimanjaro.title = @"Kilimanjaro"; | ||
kilimanjaro.image = [UIImage imageNamed:@"Hill"]; | ||
|
||
CustomMapAnnotation *mtblanc = [CustomMapAnnotation new]; | ||
mtblanc.coordinate = CLLocationCoordinate2DMake(45.8325, 6.864444); | ||
mtblanc.title = @"Mont Blanc"; | ||
mtblanc.image = [UIImage imageNamed:@"Hill"]; | ||
|
||
self.mapNode.annotations = @[brno, atlantic, kilimanjaro, mtblanc]; | ||
} | ||
|
||
-(NSString *)liveMapStr | ||
{ | ||
return _mapNode.liveMap ? @"Live Map is ON" : @"Live Map is OFF"; | ||
|
@@ -235,6 +276,21 @@ - (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeText | |
return YES; | ||
} | ||
|
||
- (MKAnnotationView *)annotationViewForAnnotation:(id<MKAnnotation>)annotation | ||
{ | ||
MKAnnotationView *av; | ||
if ([annotation isKindOfClass:[CustomMapAnnotation class]]) { | ||
av = [[MKAnnotationView alloc] init]; | ||
av.centerOffset = CGPointMake(21, 21); | ||
av.image = [(CustomMapAnnotation *)annotation image]; | ||
} else { | ||
av = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""]; | ||
} | ||
|
||
av.opaque = NO; | ||
return av; | ||
} | ||
|
||
#pragma mark - MKMapViewDelegate | ||
|
||
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { | ||
|
@@ -244,4 +300,9 @@ - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { | |
_deltaLonEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", mapView.region.span.longitudeDelta]]; | ||
} | ||
|
||
- (MKAnnotationView *)mapView:(MKMapView *)__unused mapView viewForAnnotation:(id<MKAnnotation>)annotation | ||
{ | ||
return [self annotationViewForAnnotation:annotation]; | ||
} | ||
|
||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this can be changed to:
Always get the default pin, just in case it might be used (better here than in a for loop)
Now that we have the default image, we can loop through the annotations, if the property is not set or if it returns nil, we will just use the default pin.
The idea is that if we are to use the defaultImage it is most likely going to be for more than one annotation, and this way we don't have to call the defaultPin several times since it will always return the same value anyway.
What do you think?
PS: If we won't go this way, either remove the comments that you have, or change the references from 'annotation view' to 'image for pin' or something to be consistent with the new code.