// // ApexMapView.m // OpenStreetMap // // Created by Jack on 2019/1/18. // Copyright © 2019 Jack Template. All rights reserved. // #import "ApexMapView.h" #import "UIView+RAConstraint.h" static NSString * const kTileSource = @"https://map.apexshipping.com/osm_tiles/{z}/{x}/{y}.png"; static NSString * const kOpenStreetMapURL = @"https://www.openstreetmap.org/copyright"; static NSString * const kCopyright = @"OpenStreetMap"; @interface ApexMapView () @property (nonatomic,strong) UIButton *legalBtn; @property (nonatomic,strong) MKTileOverlay *tileOverlay; @property (nonatomic,weak) id internalDelegate; @end @implementation ApexMapView #pragma mark - Initial - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setupMapView]; } return self; } - (void)awakeFromNib { [super awakeFromNib]; [self setupMapView]; } - (void)setupMapView { self.rotateEnabled = NO; [self apex_setDelegate:self]; [self addOverlay:self.tileOverlay]; } - (void)layoutSubviews { [super layoutSubviews]; NSArray *subViews = self.subviews; for (UIView *subView in subViews) { if ([subView isMemberOfClass:NSClassFromString(@"MKAttributionLabel")]) { [subView removeFromSuperview]; } else if ([subView isMemberOfClass:[UIImageView class]]) { [subView removeFromSuperview]; } } if (![subViews containsObject:self.legalBtn]) { [self setupLegalView]; } } - (void)setupLegalView { [self addSubview:self.legalBtn]; [self.legalBtn ra_applyConstraints:^(RAConstraintMaker *maker) { maker.left.ra_equalTo(self.left).ra_offset(5.0f); maker.bottom.ra_equalTo(self.bottom).ra_offset(-5.0f); }]; } #pragma mark - Override - (void)setDelegate:(id)delegate { if (delegate) { __weak typeof(delegate) weakDelegate = delegate; _internalDelegate = weakDelegate; } else { _internalDelegate = nil; } } - (void)apex_setDelegate:(id)delegate { [super setDelegate:delegate]; } #pragma mark - Getter - (MKTileOverlay *)tileOverlay { if (!_tileOverlay) { _tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:kTileSource]; _tileOverlay.canReplaceMapContent = YES; _tileOverlay.minimumZ = 1; _tileOverlay.maximumZ = 18; } return _tileOverlay; } - (UIButton *)legalBtn { if (!_legalBtn) { _legalBtn = [UIButton buttonWithType:UIButtonTypeSystem]; _legalBtn.titleLabel.font = [UIFont systemFontOfSize:12.0f]; [_legalBtn setTitle:kCopyright forState:UIControlStateNormal]; [_legalBtn addTarget:self action:@selector(legalButtonDidClick:) forControlEvents:UIControlEventTouchUpInside]; } return _legalBtn; } #pragma mark - User Action - (void)legalButtonDidClick:(id)sender { NSURL *url = [NSURL URLWithString:kOpenStreetMapURL]; if ([[UIApplication sharedApplication] canOpenURL:url]) { if (@available(iOS 10, *)) { [[UIApplication sharedApplication] openURL:url options:@{UIApplicationOpenURLOptionsSourceApplicationKey: @YES} completionHandler:^(BOOL success) { }]; } else{ [[UIApplication sharedApplication] openURL:url]; } } } #pragma mark - MKMapViewDelegate - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) { [_internalDelegate mapView:mapView regionWillChangeAnimated:animated]; } } - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) { [_internalDelegate mapView:mapView regionDidChangeAnimated:animated]; } } - (void)mapViewDidChangeVisibleRegion:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidChangeVisibleRegion:)]) { [_internalDelegate mapViewDidChangeVisibleRegion:mapView]; } } - (void)mapViewWillStartLoadingMap:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) { [_internalDelegate mapViewWillStartLoadingMap:mapView]; } } - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) { [_internalDelegate mapViewDidFinishLoadingMap:mapView]; } } - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) { [_internalDelegate mapViewDidFailLoadingMap:mapView withError:error]; } } - (void)mapViewWillStartRenderingMap:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) { [_internalDelegate mapViewWillStartRenderingMap:mapView]; } } - (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) { [_internalDelegate mapViewDidFinishRenderingMap:mapView fullyRendered:fullyRendered]; } } // mapView:viewForAnnotation: provides the view for each annotation. // This method may be called for all or some of the added annotations. // For MapKit provided annotations (eg. MKUserLocation) return nil to use the MapKit provided annotation view. - (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:viewForAnnotation:)]) { return [_internalDelegate mapView:mapView viewForAnnotation:annotation]; } return nil; } // mapView:didAddAnnotationViews: is called after the annotation views have been added and positioned in the map. // The delegate can implement this method to animate the adding of the annotations views. // Use the current positions of the annotation views as the destinations of the animation. - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddAnnotationViews:)]) { [_internalDelegate mapView:mapView didAddAnnotationViews:views]; } } #if TARGET_OS_IPHONE // mapView:annotationView:calloutAccessoryControlTapped: is called when the user taps on left & right callout accessory UIControls. - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:annotationView:calloutAccessoryControlTapped:)]) { [_internalDelegate mapView:mapView annotationView:view calloutAccessoryControlTapped:control]; } } #endif - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didSelectAnnotationView:)]) { [_internalDelegate mapView:mapView didSelectAnnotationView:view]; } } - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didDeselectAnnotationView:)]) { [_internalDelegate mapView:mapView didDeselectAnnotationView:view]; } } - (void)mapViewWillStartLocatingUser:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartLocatingUser:)]) { [_internalDelegate mapViewWillStartLocatingUser:mapView]; } } - (void)mapViewDidStopLocatingUser:(MKMapView *)mapView { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidStopLocatingUser:)]) { [_internalDelegate mapViewDidStopLocatingUser:mapView]; } } - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)]) { [_internalDelegate mapView:mapView didUpdateUserLocation:userLocation]; } } - (void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didFailToLocateUserWithError:)]) { [_internalDelegate mapView:mapView didFailToLocateUserWithError:error]; } } - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:annotationView:didChangeDragState:fromOldState:)]) { [_internalDelegate mapView:mapView annotationView:view didChangeDragState:newState fromOldState:oldState]; } } #if TARGET_OS_IPHONE - (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)]) { [_internalDelegate mapView:mapView didChangeUserTrackingMode:mode animated:animated]; } } #endif - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay { if (overlay == self.tileOverlay) { return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay]; } else { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:rendererForOverlay:)]) { return [_internalDelegate mapView:mapView rendererForOverlay:overlay]; } else { return nil; } } } - (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray *)renderers { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddOverlayRenderers:)]) { [_internalDelegate mapView:mapView didAddOverlayRenderers:renderers]; } } #if TARGET_OS_IPHONE // Prefer -mapView:rendererForOverlay: - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay { if (overlay == self.tileOverlay) { return [[MKOverlayView alloc] initWithOverlay:overlay]; } else { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:viewForOverlay:)]) { return [_internalDelegate mapView:mapView viewForOverlay:overlay]; } else { return nil; } } } // Called after the provided overlay views have been added and positioned in the map. // Prefer -mapView:didAddOverlayRenderers: - (void)mapView:(MKMapView *)mapView didAddOverlayViews:(NSArray *)overlayViews { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddOverlayViews:)]) { [_internalDelegate mapView:mapView didAddOverlayViews:overlayViews]; } } #endif // Return nil for default MKClusterAnnotation, it is illegal to return a cluster annotation not containing the identical array of member annotations given. - (MKClusterAnnotation *)mapView:(MKMapView *)mapView clusterAnnotationForMemberAnnotations:(NSArray>*)memberAnnotations { if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:clusterAnnotationForMemberAnnotations:)]) { return [_internalDelegate mapView:mapView clusterAnnotationForMemberAnnotations:memberAnnotations]; } return nil; } @end