ApexMapView.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. //
  2. // ApexMapView.m
  3. // OpenStreetMap
  4. //
  5. // Created by Jack on 2019/1/18.
  6. // Copyright © 2019 Jack Template. All rights reserved.
  7. //
  8. #import "ApexMapView.h"
  9. #import "UIView+RAConstraint.h"
  10. static NSString * const kTileSource = @"https://map.apexshipping.com/osm_tiles/{z}/{x}/{y}.png";
  11. static NSString * const kOpenStreetMapURL = @"https://www.openstreetmap.org/copyright";
  12. static NSString * const kCopyright = @"OpenStreetMap";
  13. @interface ApexMapView () <MKMapViewDelegate>
  14. @property (nonatomic,strong) UIButton *legalBtn;
  15. @property (nonatomic,strong) MKTileOverlay *tileOverlay;
  16. @property (nonatomic,weak) id<MKMapViewDelegate> internalDelegate;
  17. @end
  18. @implementation ApexMapView
  19. #pragma mark - Initial
  20. - (instancetype)initWithFrame:(CGRect)frame {
  21. if (self = [super initWithFrame:frame]) {
  22. [self setupMapView];
  23. }
  24. return self;
  25. }
  26. - (void)awakeFromNib {
  27. [super awakeFromNib];
  28. [self setupMapView];
  29. }
  30. - (void)setupMapView {
  31. self.rotateEnabled = NO;
  32. [self apex_setDelegate:self];
  33. [self addOverlay:self.tileOverlay];
  34. }
  35. - (void)layoutSubviews {
  36. [super layoutSubviews];
  37. NSArray<UIView *> *subViews = self.subviews;
  38. for (UIView *subView in subViews) {
  39. if ([subView isMemberOfClass:NSClassFromString(@"MKAttributionLabel")]) {
  40. [subView removeFromSuperview];
  41. } else if ([subView isMemberOfClass:[UIImageView class]]) {
  42. [subView removeFromSuperview];
  43. }
  44. }
  45. if (![subViews containsObject:self.legalBtn]) {
  46. [self setupLegalView];
  47. }
  48. }
  49. - (void)setupLegalView {
  50. [self addSubview:self.legalBtn];
  51. [self.legalBtn ra_applyConstraints:^(RAConstraintMaker *maker) {
  52. maker.left.ra_equalTo(self.left).ra_offset(5.0f);
  53. maker.bottom.ra_equalTo(self.bottom).ra_offset(-5.0f);
  54. }];
  55. }
  56. #pragma mark - Override
  57. - (void)setDelegate:(id<MKMapViewDelegate>)delegate {
  58. if (delegate) {
  59. __weak typeof(delegate) weakDelegate = delegate;
  60. _internalDelegate = weakDelegate;
  61. } else {
  62. _internalDelegate = nil;
  63. }
  64. }
  65. - (void)apex_setDelegate:(id<MKMapViewDelegate>)delegate {
  66. [super setDelegate:delegate];
  67. }
  68. #pragma mark - Getter
  69. - (MKTileOverlay *)tileOverlay {
  70. if (!_tileOverlay) {
  71. _tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:kTileSource];
  72. _tileOverlay.canReplaceMapContent = YES;
  73. _tileOverlay.minimumZ = 1;
  74. _tileOverlay.maximumZ = 18;
  75. }
  76. return _tileOverlay;
  77. }
  78. - (UIButton *)legalBtn {
  79. if (!_legalBtn) {
  80. _legalBtn = [UIButton buttonWithType:UIButtonTypeSystem];
  81. _legalBtn.titleLabel.font = [UIFont systemFontOfSize:12.0f];
  82. [_legalBtn setTitle:kCopyright forState:UIControlStateNormal];
  83. [_legalBtn addTarget:self action:@selector(legalButtonDidClick:) forControlEvents:UIControlEventTouchUpInside];
  84. }
  85. return _legalBtn;
  86. }
  87. #pragma mark - User Action
  88. - (void)legalButtonDidClick:(id)sender {
  89. NSURL *url = [NSURL URLWithString:kOpenStreetMapURL];
  90. if ([[UIApplication sharedApplication] canOpenURL:url]) {
  91. if (@available(iOS 10, *)) {
  92. [[UIApplication sharedApplication] openURL:url options:@{UIApplicationOpenURLOptionsSourceApplicationKey: @YES} completionHandler:^(BOOL success) {
  93. }];
  94. } else{
  95. [[UIApplication sharedApplication] openURL:url];
  96. }
  97. }
  98. }
  99. #pragma mark - MKMapViewDelegate
  100. - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
  101. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) {
  102. [_internalDelegate mapView:mapView regionWillChangeAnimated:animated];
  103. }
  104. }
  105. - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
  106. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) {
  107. [_internalDelegate mapView:mapView regionDidChangeAnimated:animated];
  108. }
  109. }
  110. - (void)mapViewDidChangeVisibleRegion:(MKMapView *)mapView {
  111. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidChangeVisibleRegion:)]) {
  112. [_internalDelegate mapViewDidChangeVisibleRegion:mapView];
  113. }
  114. }
  115. - (void)mapViewWillStartLoadingMap:(MKMapView *)mapView {
  116. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) {
  117. [_internalDelegate mapViewWillStartLoadingMap:mapView];
  118. }
  119. }
  120. - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
  121. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) {
  122. [_internalDelegate mapViewDidFinishLoadingMap:mapView];
  123. }
  124. }
  125. - (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error {
  126. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) {
  127. [_internalDelegate mapViewDidFailLoadingMap:mapView withError:error];
  128. }
  129. }
  130. - (void)mapViewWillStartRenderingMap:(MKMapView *)mapView {
  131. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) {
  132. [_internalDelegate mapViewWillStartRenderingMap:mapView];
  133. }
  134. }
  135. - (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered {
  136. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) {
  137. [_internalDelegate mapViewDidFinishRenderingMap:mapView fullyRendered:fullyRendered];
  138. }
  139. }
  140. // mapView:viewForAnnotation: provides the view for each annotation.
  141. // This method may be called for all or some of the added annotations.
  142. // For MapKit provided annotations (eg. MKUserLocation) return nil to use the MapKit provided annotation view.
  143. - (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
  144. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:viewForAnnotation:)]) {
  145. return [_internalDelegate mapView:mapView viewForAnnotation:annotation];
  146. }
  147. return nil;
  148. }
  149. // mapView:didAddAnnotationViews: is called after the annotation views have been added and positioned in the map.
  150. // The delegate can implement this method to animate the adding of the annotations views.
  151. // Use the current positions of the annotation views as the destinations of the animation.
  152. - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray<MKAnnotationView *> *)views {
  153. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddAnnotationViews:)]) {
  154. [_internalDelegate mapView:mapView didAddAnnotationViews:views];
  155. }
  156. }
  157. #if TARGET_OS_IPHONE
  158. // mapView:annotationView:calloutAccessoryControlTapped: is called when the user taps on left & right callout accessory UIControls.
  159. - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
  160. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:annotationView:calloutAccessoryControlTapped:)]) {
  161. [_internalDelegate mapView:mapView annotationView:view calloutAccessoryControlTapped:control];
  162. }
  163. }
  164. #endif
  165. - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
  166. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didSelectAnnotationView:)]) {
  167. [_internalDelegate mapView:mapView didSelectAnnotationView:view];
  168. }
  169. }
  170. - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view {
  171. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didDeselectAnnotationView:)]) {
  172. [_internalDelegate mapView:mapView didDeselectAnnotationView:view];
  173. }
  174. }
  175. - (void)mapViewWillStartLocatingUser:(MKMapView *)mapView {
  176. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewWillStartLocatingUser:)]) {
  177. [_internalDelegate mapViewWillStartLocatingUser:mapView];
  178. }
  179. }
  180. - (void)mapViewDidStopLocatingUser:(MKMapView *)mapView {
  181. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapViewDidStopLocatingUser:)]) {
  182. [_internalDelegate mapViewDidStopLocatingUser:mapView];
  183. }
  184. }
  185. - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
  186. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)]) {
  187. [_internalDelegate mapView:mapView didUpdateUserLocation:userLocation];
  188. }
  189. }
  190. - (void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error {
  191. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didFailToLocateUserWithError:)]) {
  192. [_internalDelegate mapView:mapView didFailToLocateUserWithError:error];
  193. }
  194. }
  195. - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
  196. fromOldState:(MKAnnotationViewDragState)oldState {
  197. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:annotationView:didChangeDragState:fromOldState:)]) {
  198. [_internalDelegate mapView:mapView annotationView:view didChangeDragState:newState fromOldState:oldState];
  199. }
  200. }
  201. #if TARGET_OS_IPHONE
  202. - (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
  203. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)]) {
  204. [_internalDelegate mapView:mapView didChangeUserTrackingMode:mode animated:animated];
  205. }
  206. }
  207. #endif
  208. - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay {
  209. if (overlay == self.tileOverlay) {
  210. return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];
  211. } else {
  212. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:rendererForOverlay:)]) {
  213. return [_internalDelegate mapView:mapView rendererForOverlay:overlay];
  214. } else {
  215. return nil;
  216. }
  217. }
  218. }
  219. - (void)mapView:(MKMapView *)mapView didAddOverlayRenderers:(NSArray<MKOverlayRenderer *> *)renderers {
  220. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddOverlayRenderers:)]) {
  221. [_internalDelegate mapView:mapView didAddOverlayRenderers:renderers];
  222. }
  223. }
  224. #if TARGET_OS_IPHONE
  225. // Prefer -mapView:rendererForOverlay:
  226. - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
  227. if (overlay == self.tileOverlay) {
  228. return [[MKOverlayView alloc] initWithOverlay:overlay];
  229. } else {
  230. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:viewForOverlay:)]) {
  231. return [_internalDelegate mapView:mapView viewForOverlay:overlay];
  232. } else {
  233. return nil;
  234. }
  235. }
  236. }
  237. // Called after the provided overlay views have been added and positioned in the map.
  238. // Prefer -mapView:didAddOverlayRenderers:
  239. - (void)mapView:(MKMapView *)mapView didAddOverlayViews:(NSArray *)overlayViews {
  240. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:didAddOverlayViews:)]) {
  241. [_internalDelegate mapView:mapView didAddOverlayViews:overlayViews];
  242. }
  243. }
  244. #endif
  245. // Return nil for default MKClusterAnnotation, it is illegal to return a cluster annotation not containing the identical array of member annotations given.
  246. - (MKClusterAnnotation *)mapView:(MKMapView *)mapView clusterAnnotationForMemberAnnotations:(NSArray<id<MKAnnotation>>*)memberAnnotations {
  247. if (_internalDelegate && [_internalDelegate respondsToSelector:@selector(mapView:clusterAnnotationForMemberAnnotations:)]) {
  248. return [_internalDelegate mapView:mapView clusterAnnotationForMemberAnnotations:memberAnnotations];
  249. }
  250. return nil;
  251. }
  252. @end