Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

Commit

Permalink
[ASMapNode] Add custom pin annotation for static maps (#1890)
Browse files Browse the repository at this point in the history
* Adds possibility to have custom annotation pins on static map. This resolves #1889.

* Removes wrong example for map annotations and adds some annotations to correct map example. #1889

* Static map node now uses specific property block to get annotation views.

* Changes self to strongSelf inside of the snapshotters completion block.

* MapNode: Adds statement in documentation.

* MapNode: Block for annotation view/image now returns UIImage and center offset is returned in inout param.

* MapNode and map example: Fixes from review.

* MapNode example: Gets image directly from custom annotation, without creating annotation view.
  • Loading branch information
michalziman authored and Adlai Holler committed Aug 22, 2016
1 parent c0be871 commit 873bae2
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 6 deletions.
6 changes: 6 additions & 0 deletions AsyncDisplayKit/ASMapNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ typedef NS_OPTIONS(NSUInteger, ASMapNodeShowAnnotationsOptions)
*/
@property (nonatomic, assign) ASMapNodeShowAnnotationsOptions showAnnotationsOptions;

/**
* @abstract The block which should return annotation image for static map based on provided annotation.
* @discussion This block is executed on an arbitrary serial queue. If this block is nil, standard pin is used.
*/
@property (nonatomic, copy, nullable) UIImage * _Nullable (^imageForStaticMapAnnotationBlock)(id<MKAnnotation> annotation, CGPoint *centerOffset);

@end

NS_ASSUME_NONNULL_END
Expand Down
41 changes: 36 additions & 5 deletions AsyncDisplayKit/ASMapNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 image 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;
Expand All @@ -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;
return pin.image;
}

- (void)setUpSnapshotter
{
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:self.options];
Expand Down
8 changes: 7 additions & 1 deletion examples/ASMapNode/Sample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
694993D81C8B334F00491CA5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 694993D71C8B334F00491CA5 /* ViewController.m */; };
694993DD1C8B334F00491CA5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 694993DC1C8B334F00491CA5 /* Assets.xcassets */; };
694993E01C8B334F00491CA5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 694993DE1C8B334F00491CA5 /* LaunchScreen.storyboard */; };
905C815E1D362E9400EA2625 /* CustomMapAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 905C815D1D362E9400EA2625 /* CustomMapAnnotation.m */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
15AD337503831C4D33FF8B3A /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
465082D55CCF1B0CB1AEBACC /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5E5E62821D13F39400D81E38 /* MapHandlerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapHandlerNode.h; sourceTree = "<group>"; };
5E5E62831D13F39400D81E38 /* MapHandlerNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapHandlerNode.m; sourceTree = "<group>"; };
5E5E62831D13F39400D81E38 /* MapHandlerNode.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = MapHandlerNode.m; sourceTree = "<group>"; tabWidth = 2; };
694993CD1C8B334F00491CA5 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
694993D11C8B334F00491CA5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
694993D31C8B334F00491CA5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
Expand All @@ -30,6 +31,8 @@
694993DC1C8B334F00491CA5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
694993DF1C8B334F00491CA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
694993E11C8B334F00491CA5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
905C815C1D362E9400EA2625 /* CustomMapAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomMapAnnotation.h; sourceTree = "<group>"; };
905C815D1D362E9400EA2625 /* CustomMapAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomMapAnnotation.m; sourceTree = "<group>"; };
97482F27BE2F7583EFE1BC2C /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -89,6 +92,8 @@
694993D71C8B334F00491CA5 /* ViewController.m */,
5E5E62821D13F39400D81E38 /* MapHandlerNode.h */,
5E5E62831D13F39400D81E38 /* MapHandlerNode.m */,
905C815C1D362E9400EA2625 /* CustomMapAnnotation.h */,
905C815D1D362E9400EA2625 /* CustomMapAnnotation.m */,
694993DC1C8B334F00491CA5 /* Assets.xcassets */,
694993D01C8B334F00491CA5 /* Supporting Files */,
);
Expand Down Expand Up @@ -229,6 +234,7 @@
694993D81C8B334F00491CA5 /* ViewController.m in Sources */,
694993D51C8B334F00491CA5 /* AppDelegate.m in Sources */,
694993D21C8B334F00491CA5 /* main.m in Sources */,
905C815E1D362E9400EA2625 /* CustomMapAnnotation.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
6 changes: 6 additions & 0 deletions examples/ASMapNode/Sample/Assets.xcassets/Contents.json
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"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions examples/ASMapNode/Sample/CustomMapAnnotation.h
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
22 changes: 22 additions & 0 deletions examples/ASMapNode/Sample/CustomMapAnnotation.m
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
61 changes: 61 additions & 0 deletions examples/ASMapNode/Sample/MapHandlerNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//

#import "MapHandlerNode.h"
#import "CustomMapAnnotation.h"

#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>

Expand Down Expand Up @@ -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]]) {
CustomMapAnnotation *customAnnotation = (CustomMapAnnotation *)annotation;
return customAnnotation.image;
}
}
return nil;
};

[self addAnnotations];
}

#pragma mark - Layout
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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 {
Expand All @@ -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

0 comments on commit 873bae2

Please sign in to comment.