[ASMapNode] Add custom pin annotation for static maps (#1890)

* 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.
This commit is contained in:
Michal Ziman 2016-08-22 19:44:29 +02:00 committed by Adlai Holler
parent c0be871812
commit 873bae2eed
15 changed files with 212 additions and 6 deletions

View File

@ -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

View File

@ -167,6 +167,14 @@
self.options = options;
}
- (void)setMapDelegate:(id<MKMapViewDelegate>)mapDelegate {
_mapDelegate = mapDelegate;
if (_mapView) {
_mapView.delegate = mapDelegate;
}
}
#pragma mark - Snapshotter
- (void)takeSnapshot
@ -209,15 +217,27 @@
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;
@ -235,6 +255,17 @@
}];
}
+ (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];

View File

@ -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>"; };
@ -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 */
@ -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 */,
);
@ -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;
};

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -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

View File

@ -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

View File

@ -16,6 +16,7 @@
//
#import "MapHandlerNode.h"
#import "CustomMapAnnotation.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
@ -90,6 +91,22 @@
[_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
@ -183,6 +200,30 @@
#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 @@
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 @@
_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