mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Substantially improved ASMapNode and made it a lot clearer and less complex internally.
This commit is contained in:
@@ -8,38 +8,36 @@
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import <MapKit/MapKit.h>
|
||||
@interface ASMapNode : ASControlNode
|
||||
|
||||
@interface ASMapNode : ASImageNode
|
||||
|
||||
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
This is the snapshot shot image node, this will be hidden (but not nil) when .liveMap = YES
|
||||
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) ASImageNode *mapImage;
|
||||
/**
|
||||
This is the ASDisplayNode that backs the MKMapView. This will be nil if .liveMap = NO. To access the underlying MKMapView, in order to set a delegate for example, use (MKMapView *)mapView.view;
|
||||
*/
|
||||
@property (nonatomic, readonly) ASDisplayNode *mapView;
|
||||
@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 Explicitly set the size of the map and therefore the size of ASMapNode. Defaults to CGSizeMake(constrainedSize.max.width, 256).
|
||||
@discussion If the mapSize width or height is greater than the available space, then ASMapNode will take the maximum space available.
|
||||
@result The current size of the ASMapNode.
|
||||
*/
|
||||
@property (nonatomic, assign) CGSize mapSize;
|
||||
|
||||
/**
|
||||
@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 automaticallyReloadsMapImageOnOrientationChange;
|
||||
@property (nonatomic, assign) BOOL needsMapReloadOnBoundsChange;
|
||||
|
||||
/**
|
||||
Set the delegate of the MKMapView.
|
||||
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 <MKMapViewDelegate> mapDelegate;
|
||||
|
||||
/**
|
||||
* @discussion This method adds annotations to the static map view and also to the live map view.
|
||||
* @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)addAnnotations:(NSArray *)annotations;
|
||||
- (void)setAnnotations:(NSArray *)annotations;
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,19 +14,18 @@
|
||||
@interface ASMapNode()
|
||||
{
|
||||
ASDN::RecursiveMutex _propertyLock;
|
||||
CGSize _nodeSize;
|
||||
MKMapSnapshotter *_snapshotter;
|
||||
MKMapSnapshotOptions *_options;
|
||||
CGSize _maxSize;
|
||||
NSArray *_annotations;
|
||||
ASDisplayNode *_mapNode;
|
||||
CLLocationCoordinate2D _centerCoordinateOfMap;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ASMapNode
|
||||
|
||||
@synthesize liveMap = _liveMap;
|
||||
@synthesize mapSize = _mapSize;
|
||||
@synthesize automaticallyReloadsMapImageOnOrientationChange = _automaticallyReloadsMapImageOnOrientationChange;
|
||||
@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange;
|
||||
@synthesize mapDelegate = _mapDelegate;
|
||||
|
||||
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate
|
||||
@@ -35,26 +34,23 @@
|
||||
return nil;
|
||||
}
|
||||
self.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||
_automaticallyReloadsMapImageOnOrientationChange = YES;
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
_needsMapReloadOnBoundsChange = YES;
|
||||
_liveMap = NO;
|
||||
_centerCoordinateOfMap = kCLLocationCoordinate2DInvalid;
|
||||
|
||||
_options = [[MKMapSnapshotOptions alloc] init];
|
||||
_options.region = MKCoordinateRegionMakeWithDistance(coordinate, 1000, 1000);;
|
||||
_mapImage = [[ASImageNode alloc]init];
|
||||
_mapImage.clipsToBounds = YES;
|
||||
[self addSubnode:_mapImage];
|
||||
_maxSize = self.bounds.size;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addAnnotations:(NSArray *)annotations
|
||||
- (void)setAnnotations:(NSArray *)annotations
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (annotations.count == 0) {
|
||||
return;
|
||||
}
|
||||
_annotations = [annotations copy];
|
||||
if (annotations.count != _annotations.count && _mapImage.image) {
|
||||
if (annotations.count != _annotations.count) {
|
||||
// Redraw
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
@@ -63,8 +59,9 @@
|
||||
- (void)setUpSnapshotter
|
||||
{
|
||||
if (!_snapshotter) {
|
||||
_options.size = _nodeSize;
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,60 +74,42 @@
|
||||
- (void)setLiveMap:(BOOL)liveMap
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (liveMap == _liveMap) return;
|
||||
if (liveMap == _liveMap) {
|
||||
return;
|
||||
}
|
||||
_liveMap = liveMap;
|
||||
liveMap ? [self addLiveMap] : [self removeLiveMap];
|
||||
}
|
||||
|
||||
- (CGSize)mapSize
|
||||
|
||||
- (BOOL)needsMapReloadOnBoundsChange
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _mapSize;
|
||||
return _needsMapReloadOnBoundsChange;
|
||||
}
|
||||
|
||||
- (void)setMapSize:(CGSize)mapSize
|
||||
- (void)setNeedsMapReloadOnBoundsChange:(BOOL)needsMapReloadOnBoundsChange
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (CGSizeEqualToSize(mapSize,_mapSize)) {
|
||||
return;
|
||||
}
|
||||
_mapSize = mapSize;
|
||||
_nodeSize = _mapSize;
|
||||
_automaticallyReloadsMapImageOnOrientationChange = NO;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (BOOL)automaticallyReloadsMapImageOnOrientationChange
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
return _automaticallyReloadsMapImageOnOrientationChange;
|
||||
}
|
||||
|
||||
- (void)setAutomaticallyReloadsMapImageOnOrientationChange:(BOOL)automaticallyReloadsMapImageOnOrientationChange
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (_automaticallyReloadsMapImageOnOrientationChange == automaticallyReloadsMapImageOnOrientationChange) {
|
||||
return;
|
||||
}
|
||||
_automaticallyReloadsMapImageOnOrientationChange = automaticallyReloadsMapImageOnOrientationChange;
|
||||
|
||||
_needsMapReloadOnBoundsChange = needsMapReloadOnBoundsChange;
|
||||
}
|
||||
|
||||
- (void)fetchData
|
||||
{
|
||||
[super fetchData];
|
||||
[self setUpSnapshotter];
|
||||
[self takeSnapshot];
|
||||
if (_liveMap && !_mapNode) {
|
||||
[self addLiveMap];
|
||||
}
|
||||
else {
|
||||
[self setUpSnapshotter];
|
||||
[self takeSnapshot];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
{
|
||||
[super clearFetchedData];
|
||||
if (_mapView) {
|
||||
[_mapView removeFromSupernode];
|
||||
_mapView = nil;
|
||||
}
|
||||
_mapImage.image = nil;
|
||||
[self removeLiveMap];
|
||||
}
|
||||
|
||||
- (void)takeSnapshot
|
||||
@@ -141,29 +120,31 @@
|
||||
UIImage *image = snapshot.image;
|
||||
CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);
|
||||
|
||||
// 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;
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
|
||||
[image drawAtPoint:CGPointMake(0, 0)];
|
||||
|
||||
for (id<MKAnnotation>annotation in _annotations)
|
||||
{
|
||||
CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
|
||||
if (CGRectContainsPoint(finalImageRect, point))
|
||||
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 (id<MKAnnotation>annotation in _annotations)
|
||||
{
|
||||
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];
|
||||
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();
|
||||
_mapImage.image = finalImage;
|
||||
self.image = finalImage;
|
||||
}
|
||||
}];
|
||||
}
|
||||
@@ -172,7 +153,7 @@
|
||||
- (void)resetSnapshotter
|
||||
{
|
||||
if (!_snapshotter.isLoading) {
|
||||
_options.size = _nodeSize;
|
||||
_options.size = self.calculatedSize;
|
||||
_snapshotter = [[MKMapSnapshotter alloc] initWithOptions:_options];
|
||||
}
|
||||
}
|
||||
@@ -180,52 +161,45 @@
|
||||
#pragma mark - Action
|
||||
- (void)addLiveMap
|
||||
{
|
||||
if (self.isNodeLoaded && !_mapView) {
|
||||
_mapView = [[ASDisplayNode alloc]initWithViewBlock:^UIView *{
|
||||
MKMapView *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;
|
||||
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:_mapView];
|
||||
_mapImage.hidden = YES;
|
||||
[self addSubnode:_mapNode];
|
||||
|
||||
if (CLLocationCoordinate2DIsValid(_centerCoordinateOfMap)) {
|
||||
[_mapView setCenterCoordinate:_centerCoordinateOfMap];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeLiveMap
|
||||
{
|
||||
if (_mapView) {
|
||||
[_mapView removeFromSupernode];
|
||||
if (_mapNode) {
|
||||
_centerCoordinateOfMap = _mapView.centerCoordinate;
|
||||
[_mapNode removeFromSupernode];
|
||||
_mapView = nil;
|
||||
_mapImage.hidden = NO;
|
||||
_mapNode = nil;
|
||||
}
|
||||
self.image = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
_nodeSize = CGSizeEqualToSize(CGSizeZero, _mapSize) ? CGSizeMake(constrainedSize.width, _options.size.height) : _mapSize;
|
||||
if (_mapImage) {
|
||||
[_mapImage calculateSizeThatFits:_nodeSize];
|
||||
}
|
||||
return _nodeSize;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
MKMapView *mapView = (MKMapView *)_mapView.view;
|
||||
mapView.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height);
|
||||
_mapView.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height);
|
||||
}
|
||||
else {
|
||||
_mapImage.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height);
|
||||
if (!CGSizeEqualToSize(_maxSize, self.bounds.size)) {
|
||||
_mapImage.preferredFrameSize = self.bounds.size;
|
||||
_maxSize = self.bounds.size;
|
||||
if (_automaticallyReloadsMapImageOnOrientationChange && _mapImage.image) {
|
||||
// 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];
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
05E2127E19D4DB510098F589 /* Frameworks */,
|
||||
05E2127F19D4DB510098F589 /* Resources */,
|
||||
F012A6F39E0149F18F564F50 /* Copy Pods Resources */,
|
||||
860D1494A00C2E990C93A4D9 /* Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -184,6 +185,21 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
860D1494A00C2E990C93A4D9 /* Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
||||
@@ -29,6 +29,7 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
ASNetworkImageNode *_imageNode;
|
||||
ASTextNode *_textNode;
|
||||
ASDisplayNode *_divider;
|
||||
ASMapNode *_map;
|
||||
BOOL _isImageEnlarged;
|
||||
BOOL _swappedTextAndImage;
|
||||
}
|
||||
@@ -89,6 +90,22 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
[_imageNode addTarget:self action:@selector(toggleNodesSwap) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[self addSubnode:_imageNode];
|
||||
|
||||
MKPointAnnotation *point1 = [[MKPointAnnotation alloc]init];
|
||||
point1.coordinate = CLLocationCoordinate2DMake(55.864237, -4.251806);
|
||||
point1.title = @"Best fish & chip shop";
|
||||
point1.subtitle = @"Everrrrrr";
|
||||
|
||||
|
||||
MKPointAnnotation *point2 = [[MKPointAnnotation alloc]init];
|
||||
point2.coordinate = CLLocationCoordinate2DMake(55.861, -4.251806);
|
||||
point2.title = @"The 2nd Best fish & chip shop";
|
||||
point2.subtitle = @"Everrrrrr";
|
||||
|
||||
_map = [[ASMapNode alloc]initWithCoordinate:CLLocationCoordinate2DMake(55.864237, -4.251806)];
|
||||
[_map addTarget:self action:@selector(makeMapInteractive) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[_map setAnnotations:@[point1,point2]];
|
||||
[self addSubnode:_map];
|
||||
|
||||
// lorem ipsum text, plus some nice styling
|
||||
_textNode = [[ASTextNode alloc] init];
|
||||
_textNode.attributedString = [[NSAttributedString alloc] initWithString:[self kittyIpsum]
|
||||
@@ -131,16 +148,25 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
NSParagraphStyleAttributeName: style };
|
||||
}
|
||||
|
||||
- (void)makeMapInteractive
|
||||
{
|
||||
[_map setLiveMap:YES];
|
||||
}
|
||||
|
||||
#if UseAutomaticLayout
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
_imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize);
|
||||
_textNode.flexShrink = YES;
|
||||
|
||||
ASRatioLayoutSpec *ratioSpec = [[ASRatioLayoutSpec alloc]init];
|
||||
ratioSpec.ratio = 0.5;
|
||||
ratioSpec.child = _map;
|
||||
|
||||
ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init];
|
||||
stackSpec.direction = ASStackLayoutDirectionHorizontal;
|
||||
stackSpec.direction = ASStackLayoutDirectionVertical;
|
||||
stackSpec.spacing = kInnerPadding;
|
||||
[stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]];
|
||||
[stackSpec setChildren:!_swappedTextAndImage ? @[ratioSpec,_imageNode, _textNode] : @[ratioSpec,_textNode, _imageNode]];
|
||||
|
||||
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
|
||||
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding);
|
||||
|
||||
Reference in New Issue
Block a user