Skip to content

Commit fe06f21

Browse files
Added an ASMapNode to AsyncDisplayKit
1 parent 5b1e214 commit fe06f21

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed

AsyncDisplayKit.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@
211211
509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
212212
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; };
213213
6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
214+
92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; };
215+
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */; };
214216
9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; };
215217
9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; };
216218
9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -599,6 +601,8 @@
599601
4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASFlowLayoutController.mm; sourceTree = "<group>"; };
600602
4640521D1A3F83C40061C0BA /* ASLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutController.h; sourceTree = "<group>"; };
601603
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = "<group>"; };
604+
92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMapNode.h; sourceTree = "<group>"; };
605+
92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMapNode.mm; sourceTree = "<group>"; };
602606
9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = "<group>"; };
603607
9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASAsciiArtBoxCreator.h; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h; sourceTree = "<group>"; };
604608
9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASAsciiArtBoxCreator.m; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m; sourceTree = "<group>"; };
@@ -787,6 +791,8 @@
787791
058D09B1195D04C000B7D73C /* AsyncDisplayKit */ = {
788792
isa = PBXGroup;
789793
children = (
794+
92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */,
795+
92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */,
790796
055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */,
791797
AC6456071B0A335000CF11B8 /* ASCellNode.m */,
792798
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */,
@@ -1194,6 +1200,7 @@
11941200
ACF6ED4F1B17847A00DA7C62 /* ASStackPositionedLayout.h in Headers */,
11951201
ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */,
11961202
9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */,
1203+
92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */,
11971204
ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */,
11981205
055F1A3419ABD3E3004DAFF1 /* ASTableView.h in Headers */,
11991206
251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */,
@@ -1547,6 +1554,7 @@
15471554
AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */,
15481555
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */,
15491556
18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */,
1557+
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */,
15501558
AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */,
15511559
205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */,
15521560
058D0A13195D050800B7D73C /* ASControlNode.m in Sources */,

AsyncDisplayKit/ASMapNode.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
#import <AsyncDisplayKit/AsyncDisplayKit.h>
9+
#import <MapKit/MapKit.h>
10+
@interface ASMapNode : ASControlNode
11+
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate NS_DESIGNATED_INITIALIZER;
12+
13+
@property (nonatomic, readonly) ASImageNode *mapImage;
14+
@property (nonatomic, readonly) ASDisplayNode *liveMap;
15+
/**
16+
Whether the map snapshot should turn into a MKMapView when tapped on. Defaults to YES.
17+
*/
18+
@property (nonatomic, assign) BOOL hasLiveMap;
19+
/**
20+
@abstract Explicitly set the size of the map and therefore the size of ASMapNode. Defaults to CGSizeMake(constrainedSize.max.width, 256).
21+
@discussion If the mapSize width or height is greater than the available space, then ASMapNode will take the maximum space available.
22+
@result The current size of the ASMapNode.
23+
*/
24+
@property (nonatomic, assign) CGSize mapSize;
25+
/**
26+
@abstract Whether ASMapNode should automatically request a new map snapshot to correspond to the new node size. Defaults to YES.
27+
@discussion If mapSize is set then this will be set to NO, since the size will be the same in all orientations.
28+
*/
29+
@property (nonatomic, assign) BOOL automaticallyReloadsMapImageOnOrientationChange;
30+
/**
31+
Set the delegate of the MKMapView.
32+
*/
33+
@property (nonatomic, weak) id <MKMapViewDelegate> mapDelegate;
34+
/**
35+
* @discussion This method adds annotations to the static map view and also to the live map view.
36+
* @param annotations An array of objects that conform to the MKAnnotation protocol
37+
*/
38+
- (void)addAnnotations:(NSArray *)annotations;
39+
@end

AsyncDisplayKit/ASMapNode.mm

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/* Copyright (c) 2014-present, Facebook, Inc.
2+
* All rights reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree. An additional grant
6+
* of patent rights can be found in the PATENTS file in the same directory.
7+
*/
8+
9+
#import "ASMapNode.h"
10+
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
11+
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
12+
#import <AsyncDisplayKit/ASThread.h>
13+
14+
@interface ASMapNode()
15+
{
16+
ASDN::RecursiveMutex _propertyLock;
17+
CGSize _nodeSize;
18+
MKMapSnapshotter *_snapshotter;
19+
MKMapSnapshotOptions *_options;
20+
CGSize _maxSize;
21+
NSArray *_annotations;
22+
}
23+
@end
24+
25+
@implementation ASMapNode
26+
27+
@synthesize hasLiveMap = _hasLiveMap;
28+
@synthesize mapSize = _mapSize;
29+
@synthesize automaticallyReloadsMapImageOnOrientationChange = _automaticallyReloadsMapImageOnOrientationChange;
30+
@synthesize mapDelegate = _mapDelegate;
31+
32+
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate
33+
{
34+
if (!(self = [super init])) {
35+
return nil;
36+
}
37+
self.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
38+
_hasLiveMap = YES;
39+
_automaticallyReloadsMapImageOnOrientationChange = YES;
40+
_options = [[MKMapSnapshotOptions alloc] init];
41+
_options.region = MKCoordinateRegionMakeWithDistance(coordinate, 1000, 1000);;
42+
43+
_mapImage = [[ASImageNode alloc]init];
44+
_mapImage.clipsToBounds = YES;
45+
[self addSubnode:_mapImage];
46+
[self updateGesture];
47+
_maxSize = self.bounds.size;
48+
return self;
49+
}
50+
51+
- (void)addAnnotations:(NSArray *)annotations
52+
{
53+
ASDN::MutexLocker l(_propertyLock);
54+
if (annotations.count == 0) {
55+
return;
56+
}
57+
_annotations = [annotations copy];
58+
if (annotations.count != _annotations.count && _mapImage.image) {
59+
// Redraw
60+
[self setNeedsDisplay];
61+
}
62+
}
63+
64+
- (void)setUpSnapshotter
65+
{
66+
if (!_snapshotter) {
67+
_options.size = _nodeSize;
68+
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
69+
}
70+
}
71+
72+
- (BOOL)hasLiveMap
73+
{
74+
ASDN::MutexLocker l(_propertyLock);
75+
return _hasLiveMap;
76+
}
77+
78+
- (void)setHasLiveMap:(BOOL)hasLiveMap
79+
{
80+
ASDN::MutexLocker l(_propertyLock);
81+
if (hasLiveMap == _hasLiveMap)
82+
return;
83+
84+
_hasLiveMap = hasLiveMap;
85+
[self updateGesture];
86+
}
87+
88+
- (CGSize)mapSize
89+
{
90+
ASDN::MutexLocker l(_propertyLock);
91+
return _mapSize;
92+
}
93+
94+
- (void)setMapSize:(CGSize)mapSize
95+
{
96+
ASDN::MutexLocker l(_propertyLock);
97+
if (CGSizeEqualToSize(mapSize,_mapSize)) {
98+
return;
99+
}
100+
_mapSize = mapSize;
101+
_nodeSize = _mapSize;
102+
_automaticallyReloadsMapImageOnOrientationChange = NO;
103+
[self setNeedsLayout];
104+
}
105+
106+
- (BOOL)automaticallyReloadsMapImageOnOrientationChange
107+
{
108+
ASDN::MutexLocker l(_propertyLock);
109+
return _automaticallyReloadsMapImageOnOrientationChange;
110+
}
111+
112+
- (void)setAutomaticallyReloadsMapImageOnOrientationChange:(BOOL)automaticallyReloadsMapImageOnOrientationChange
113+
{
114+
ASDN::MutexLocker l(_propertyLock);
115+
if (_automaticallyReloadsMapImageOnOrientationChange == automaticallyReloadsMapImageOnOrientationChange) {
116+
return;
117+
}
118+
_automaticallyReloadsMapImageOnOrientationChange = automaticallyReloadsMapImageOnOrientationChange;
119+
120+
}
121+
122+
- (void)updateGesture
123+
{
124+
_hasLiveMap ? [self addTarget:self action:@selector(showLiveMap) forControlEvents:ASControlNodeEventTouchUpInside] : [self removeTarget:self action:@selector(showLiveMap) forControlEvents:ASControlNodeEventTouchUpInside];
125+
}
126+
127+
- (void)fetchData
128+
{
129+
[super fetchData];
130+
[self setUpSnapshotter];
131+
[self takeSnapshot];
132+
}
133+
134+
- (void)clearFetchedData
135+
{
136+
[super clearFetchedData];
137+
if (_liveMap) {
138+
[_liveMap removeFromSupernode];
139+
_liveMap = nil;
140+
}
141+
_mapImage.image = nil;
142+
}
143+
144+
- (void)takeSnapshot
145+
{
146+
if (!_snapshotter.isLoading) {
147+
[_snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
148+
if (!error) {
149+
UIImage *image = snapshot.image;
150+
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
151+
152+
// Get a standard annotation view pin. Future implementations should use a custom annotation image property.
153+
MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
154+
UIImage *pinImage = pin.image;
155+
156+
UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
157+
[image drawAtPoint:CGPointMake(0, 0)];
158+
159+
for (id<MKAnnotation>annotation in _annotations)
160+
{
161+
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
162+
if (CGRectContainsPoint(finalImageRect, point))
163+
{
164+
CGPoint pinCenterOffset = pin.centerOffset;
165+
point.x -= pin.bounds.size.width / 2.0;
166+
point.y -= pin.bounds.size.height / 2.0;
167+
point.x += pinCenterOffset.x;
168+
point.y += pinCenterOffset.y;
169+
[pinImage drawAtPoint:point];
170+
}
171+
}
172+
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
173+
UIGraphicsEndImageContext();
174+
_mapImage.image = finalImage;
175+
}
176+
}];
177+
}
178+
}
179+
180+
- (void)resetSnapshotter
181+
{
182+
if (!_snapshotter.isLoading) {
183+
_options.size = _nodeSize;
184+
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
185+
}
186+
}
187+
188+
#pragma mark - Action
189+
- (void)showLiveMap
190+
{
191+
if (self.isNodeLoaded && !_liveMap) {
192+
_liveMap = [[ASDisplayNode alloc]initWithViewBlock:^UIView *{
193+
MKMapView *mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height)];
194+
mapView.delegate = _mapDelegate;
195+
[mapView setRegion:_options.region];
196+
[mapView addAnnotations:_annotations];
197+
return mapView;
198+
}];
199+
[self addSubnode:_liveMap];
200+
_mapImage.image = nil;
201+
}
202+
}
203+
204+
#pragma mark - Layout
205+
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
206+
{
207+
_nodeSize = CGSizeEqualToSize(CGSizeZero, _mapSize) ? CGSizeMake(constrainedSize.width, _options.size.height) : _mapSize;
208+
if (_mapImage) {
209+
[_mapImage calculateSizeThatFits:_nodeSize];
210+
}
211+
return _nodeSize;
212+
}
213+
214+
// Layout isn't usually needed in the box model, but since we are making use of MKMapView which is hidden in an ASDisplayNode this is preferred.
215+
- (void)layout
216+
{
217+
[super layout];
218+
if (_liveMap) {
219+
MKMapView *mapView = (MKMapView *)_liveMap.view;
220+
mapView.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height);
221+
}
222+
else {
223+
_mapImage.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height);
224+
if (!CGSizeEqualToSize(_maxSize, self.bounds.size)) {
225+
_mapImage.preferredFrameSize = self.bounds.size;
226+
_maxSize = self.bounds.size;
227+
if (_automaticallyReloadsMapImageOnOrientationChange && _mapImage.image) {
228+
[self resetSnapshotter];
229+
[self takeSnapshot];
230+
}
231+
}
232+
}
233+
}
234+
235+
@end

0 commit comments

Comments
 (0)