diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index fb9a6ea013..4e7bbcaed0 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |spec| ] spec.frameworks = 'AssetsLibrary' - spec.weak_frameworks = 'Photos' + spec.weak_frameworks = 'Photos','MapKit' # ASDealloc2MainObject must be compiled with MRR spec.requires_arc = true diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index a769a8fc6d..9d4582442c 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -256,6 +256,13 @@ 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */; }; + 92DD2FE61BF4D05E0074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; }; + 92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */; }; + 92DD2FE81BF4D0A80074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92DD2FE91BF4D4870074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 92DD2FEA1BF4D49B0074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; }; 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; }; 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -652,7 +659,10 @@ 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASFlowLayoutController.h; sourceTree = ""; }; 4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASFlowLayoutController.mm; sourceTree = ""; }; 4640521D1A3F83C40061C0BA /* ASLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutController.h; sourceTree = ""; }; - 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = AsyncDisplayKit.h; sourceTree = ""; }; + 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = ""; }; + 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMapNode.h; sourceTree = ""; }; + 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMapNode.mm; sourceTree = ""; }; + 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASAsciiArtBoxCreator.h; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h; sourceTree = ""; }; 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASAsciiArtBoxCreator.m; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m; sourceTree = ""; }; @@ -746,6 +756,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 92DD2FE91BF4D4870074C9DD /* MapKit.framework in Frameworks */, 051943151A1575670030A7D0 /* Photos.framework in Frameworks */, 051943131A1575630030A7D0 /* AssetsLibrary.framework in Frameworks */, 058D09B0195D04C000B7D73C /* Foundation.framework in Frameworks */, @@ -756,6 +767,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 92DD2FEA1BF4D49B0074C9DD /* MapKit.framework in Frameworks */, 0515EA221A1576A100BA8B9A /* AssetsLibrary.framework in Frameworks */, 0515EA211A15769900BA8B9A /* Photos.framework in Frameworks */, 058D09BE195D04C000B7D73C /* XCTest.framework in Frameworks */, @@ -770,6 +782,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 92DD2FE61BF4D05E0074C9DD /* MapKit.framework in Frameworks */, B350625F1B0111800018CF92 /* Foundation.framework in Frameworks */, B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */, B350625D1B0111740018CF92 /* Photos.framework in Frameworks */, @@ -829,6 +842,7 @@ 058D09AE195D04C000B7D73C /* Frameworks */ = { isa = PBXGroup; children = ( + 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */, 051943141A1575670030A7D0 /* Photos.framework */, 051943121A1575630030A7D0 /* AssetsLibrary.framework */, 058D09AF195D04C000B7D73C /* Foundation.framework */, @@ -842,6 +856,8 @@ 058D09B1195D04C000B7D73C /* AsyncDisplayKit */ = { isa = PBXGroup; children = ( + 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */, + 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */, 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */, AC6456071B0A335000CF11B8 /* ASCellNode.m */, 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */, @@ -1188,6 +1204,7 @@ buildActionMask = 2147483647; files = ( 257754C21BEE458E00737CA5 /* ASTextKitCoreTextAdditions.h in Headers */, + 92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */, AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */, 058D0A71195D05F800B7D73C /* _AS-objc-internal.h in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, @@ -1406,6 +1423,7 @@ 254C6B761BF94DF4003EC431 /* ASTextNodeTypes.h in Headers */, 34EFC7711B701CFF00AD841F /* ASStackLayoutSpec.h in Headers */, 2767E9411BB19BD600EA9B77 /* ASViewController.h in Headers */, + 92DD2FE81BF4D0A80074C9DD /* ASMapNode.h in Headers */, 044284FE1BAA387800D16268 /* ASStackLayoutSpecUtilities.h in Headers */, 34EFC7751B701D2400AD841F /* ASStackPositionedLayout.h in Headers */, 34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */, @@ -1634,6 +1652,7 @@ AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */, ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */, 18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */, + 92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */, AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */, 205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */, 058D0A13195D050800B7D73C /* ASControlNode.m in Sources */, @@ -1742,6 +1761,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */, 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */, 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */, diff --git a/AsyncDisplayKit/ASMapNode.h b/AsyncDisplayKit/ASMapNode.h new file mode 100644 index 0000000000..e5fcbd4921 --- /dev/null +++ b/AsyncDisplayKit/ASMapNode.h @@ -0,0 +1,43 @@ +/* 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. + */ + +#import +#import + +@interface ASMapNode : ASImageNode + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate NS_DESIGNATED_INITIALIZER; + +/** + This is the MKMapView that is the live map part of ASMapNode. This will be nil if .liveMap = NO. Note, MKMapView is *not* thread-safe. + */ +@property (nonatomic, readonly) MKMapView *mapView; + +/** + Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. + */ +@property (nonatomic, assign, getter=isLiveMap) BOOL liveMap; + +/** + @abstract Whether ASMapNode should automatically request a new map snapshot to correspond to the new node size. Defaults to YES. + @discussion If mapSize is set then this will be set to NO, since the size will be the same in all orientations. + */ +@property (nonatomic, assign) BOOL needsMapReloadOnBoundsChange; + +/** + Set the delegate of the MKMapView. This can be set even before mapView is created and will be set on the map in the case that the liveMap mode is engaged. + */ +@property (nonatomic, weak) id mapDelegate; + +/** + * @discussion This method set the annotations of the static map view and also to the live map view. Passing an empty array clears the map of any annotations. + * @param annotations An array of objects that conform to the MKAnnotation protocol + */ +- (void)setAnnotations:(NSArray *)annotations; + +@end diff --git a/AsyncDisplayKit/ASMapNode.mm b/AsyncDisplayKit/ASMapNode.mm new file mode 100644 index 0000000000..311000362e --- /dev/null +++ b/AsyncDisplayKit/ASMapNode.mm @@ -0,0 +1,210 @@ +/* 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. + */ + +#import "ASMapNode.h" +#import +#import +#import + +@interface ASMapNode() +{ + ASDN::RecursiveMutex _propertyLock; + MKMapSnapshotter *_snapshotter; + MKMapSnapshotOptions *_options; + NSArray *_annotations; + ASDisplayNode *_mapNode; + CLLocationCoordinate2D _centerCoordinateOfMap; +} +@end + +@implementation ASMapNode + +@synthesize liveMap = _liveMap; +@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange; +@synthesize mapDelegate = _mapDelegate; + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate +{ + if (!(self = [super init])) { + return nil; + } + self.backgroundColor = ASDisplayNodeDefaultPlaceholderColor(); + self.clipsToBounds = YES; + + _needsMapReloadOnBoundsChange = YES; + _liveMap = NO; + _centerCoordinateOfMap = kCLLocationCoordinate2DInvalid; + + _options = [[MKMapSnapshotOptions alloc] init]; + _options.region = MKCoordinateRegionMakeWithDistance(coordinate, 1000, 1000);; + + return self; +} + +- (void)setAnnotations:(NSArray *)annotations +{ + ASDN::MutexLocker l(_propertyLock); + _annotations = [annotations copy]; + if (annotations.count != _annotations.count) { + // Redraw + [self setNeedsDisplay]; + } +} + +- (void)setUpSnapshotter +{ + if (!_snapshotter) { + ASDisplayNodeAssert(!CGSizeEqualToSize(CGSizeZero, self.calculatedSize), @"self.calculatedSize can not be zero. Make sure that you are setting a preferredFrameSize or wrapping ASMapNode in a ASRatioLayoutSpec or similar."); + _options.size = self.calculatedSize; + _snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options]; + } +} + +- (BOOL)isLiveMap +{ + ASDN::MutexLocker l(_propertyLock); + return _liveMap; +} + +- (void)setLiveMap:(BOOL)liveMap +{ + ASDN::MutexLocker l(_propertyLock); + if (liveMap == _liveMap) { + return; + } + _liveMap = liveMap; + liveMap ? [self addLiveMap] : [self removeLiveMap]; +} + + +- (BOOL)needsMapReloadOnBoundsChange +{ + ASDN::MutexLocker l(_propertyLock); + return _needsMapReloadOnBoundsChange; +} + +- (void)setNeedsMapReloadOnBoundsChange:(BOOL)needsMapReloadOnBoundsChange +{ + ASDN::MutexLocker l(_propertyLock); + _needsMapReloadOnBoundsChange = needsMapReloadOnBoundsChange; +} + +- (void)fetchData +{ + [super fetchData]; + if (_liveMap && !_mapNode) { + [self addLiveMap]; + } + else { + [self setUpSnapshotter]; + [self takeSnapshot]; + } +} + +- (void)clearFetchedData +{ + [super clearFetchedData]; + [self removeLiveMap]; +} + +- (void)takeSnapshot +{ + if (!_snapshotter.isLoading) { + [_snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) { + if (!error) { + UIImage *image = snapshot.image; + CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height); + + UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale); + [image drawAtPoint:CGPointMake(0, 0)]; + + if (_annotations.count > 0 ) { + // 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; + for (idannotation in _annotations) + { + CGPoint point = [snapshot pointForCoordinate:annotation.coordinate]; + if (CGRectContainsPoint(finalImageRect, point)) + { + CGPoint pinCenterOffset = pin.centerOffset; + point.x -= pin.bounds.size.width / 2.0; + point.y -= pin.bounds.size.height / 2.0; + point.x += pinCenterOffset.x; + point.y += pinCenterOffset.y; + [pinImage drawAtPoint:point]; + } + } + } + + UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + self.image = finalImage; + } + }]; + } +} + +- (void)resetSnapshotter +{ + if (!_snapshotter.isLoading) { + _options.size = self.calculatedSize; + _snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options]; + } +} + +#pragma mark - Action +- (void)addLiveMap +{ + if (self.isNodeLoaded && !_mapNode) { + _mapNode = [[ASDisplayNode alloc]initWithViewBlock:^UIView *{ + _mapView = [[MKMapView alloc]initWithFrame:CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height)]; + _mapView.delegate = _mapDelegate; + [_mapView setRegion:_options.region]; + [_mapView addAnnotations:_annotations]; + return _mapView; + }]; + [self addSubnode:_mapNode]; + + if (CLLocationCoordinate2DIsValid(_centerCoordinateOfMap)) { + [_mapView setCenterCoordinate:_centerCoordinateOfMap]; + } + } +} + +- (void)removeLiveMap +{ + if (_mapNode) { + _centerCoordinateOfMap = _mapView.centerCoordinate; + [_mapNode removeFromSupernode]; + _mapView = nil; + _mapNode = nil; + } + self.image = nil; +} + +#pragma mark - Layout +// 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. +- (void)layout +{ + [super layout]; + if (_mapView) { + _mapView.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height); + } + else { + // If our bounds.size is different from our current snapshot size, then let's request a new image from MKMapSnapshotter. + if (!CGSizeEqualToSize(_options.size, self.bounds.size)) { + if (_needsMapReloadOnBoundsChange && self.image) { + [self resetSnapshotter]; + [self takeSnapshot]; + } + } + } +} + +@end diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/AsyncDisplayKit/AsyncDisplayKit.h index 1c592134cb..7040834238 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/AsyncDisplayKit/AsyncDisplayKit.h @@ -13,6 +13,7 @@ #import #import #import +#import #import diff --git a/README.md b/README.md index 2a249299a1..f7743eced1 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ pod 'AsyncDisplayKit' (ASDK can also be used as a regular static library: Copy the project to your codebase manually, adding `AsyncDisplayKit.xcodeproj` to your workspace. Add -`libAsyncDisplayKit.a`, AssetsLibrary, and Photos to the "Link Binary With +`libAsyncDisplayKit.a`, MapKit, AssetsLibrary, and Photos to the "Link Binary With Libraries" build phase. Include `-lc++ -ObjC` in your project linker flags.) Import the framework header, or create an [Objective-C bridging diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj index 3a7e63f9d1..aaaa497852 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 058969281ABCE1750059CE2A /* libAsyncDisplayKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 058969271ABCE1750059CE2A /* libAsyncDisplayKit.a */; }; 0589692A1ABCE17C0059CE2A /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058969291ABCE17C0059CE2A /* AssetsLibrary.framework */; }; 0589692C1ABCE1820059CE2A /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0589692B1ABCE1820059CE2A /* Photos.framework */; }; + 92DD2FEC1BF4D8BB0074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FEB1BF4D8BB0074C9DD /* MapKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -32,6 +33,7 @@ 058969271ABCE1750059CE2A /* libAsyncDisplayKit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libAsyncDisplayKit.a; path = "../../build/Debug-iphoneos/libAsyncDisplayKit.a"; sourceTree = ""; }; 058969291ABCE17C0059CE2A /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 0589692B1ABCE1820059CE2A /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; }; + 92DD2FEB1BF4D8BB0074C9DD /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -39,6 +41,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 92DD2FEC1BF4D8BB0074C9DD /* MapKit.framework in Frameworks */, 0589692C1ABCE1820059CE2A /* Photos.framework in Frameworks */, 0589692A1ABCE17C0059CE2A /* AssetsLibrary.framework in Frameworks */, 058969281ABCE1750059CE2A /* libAsyncDisplayKit.a in Frameworks */, @@ -51,6 +54,7 @@ 058968E61ABCE06E0059CE2A = { isa = PBXGroup; children = ( + 92DD2FEB1BF4D8BB0074C9DD /* MapKit.framework */, 0589692B1ABCE1820059CE2A /* Photos.framework */, 058969291ABCE17C0059CE2A /* AssetsLibrary.framework */, 058969271ABCE1750059CE2A /* libAsyncDisplayKit.a */,