Browse Source

添加IOS地图和导航

i2edu 5 years ago
parent
commit
41041ea87b

+ 32 - 9
.idea/workspace.xml

@@ -2,8 +2,25 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="cc871a7e-676b-4f6d-9e12-c09ccbaf84b8" name="Default Changelist" comment="">
+      <change afterPath="$PROJECT_DIR$/ios/Assets/Images/dog.png" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Assets/Images/i2.png" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Classes/CustomAnnotation.h" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Classes/CustomAnnotation.m" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Classes/CustomCalloutView.h" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Classes/CustomCalloutView.m" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Classes/FlutterAmapView.h" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/ios/Classes/FlutterAmapView.m" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/amap_location.iml" beforeDir="false" afterPath="$PROJECT_DIR$/amap_location.iml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Runner.xcodeproj/project.pbxproj" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/example/ios/Runner/Info.plist" beforeDir="false" afterPath="$PROJECT_DIR$/example/ios/Runner/Info.plist" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/example/lib/map_util.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/map_util.dart" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/example/lib/map_view_page.dart" beforeDir="false" afterPath="$PROJECT_DIR$/example/lib/map_view_page.dart" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/ios/Classes/AmapLocationPlugin.h" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/AmapLocationPlugin.h" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/ios/Classes/AmapLocationPlugin.m" beforeDir="false" afterPath="$PROJECT_DIR$/ios/Classes/AmapLocationPlugin.m" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/ios/amap_location.podspec" beforeDir="false" afterPath="$PROJECT_DIR$/ios/amap_location.podspec" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/lib/amap_location.dart" beforeDir="false" afterPath="$PROJECT_DIR$/lib/amap_location.dart" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/pubspec.lock" beforeDir="false" afterPath="$PROJECT_DIR$/pubspec.lock" afterDir="false" />
     </list>
     <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
     <option name="SHOW_DIALOG" value="false" />
@@ -32,11 +49,14 @@
     <property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
     <property name="dart.analysis.tool.window.force.activate" value="false" />
     <property name="io.flutter.reload.alreadyRun" value="true" />
-    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/ios/Assets/Images" />
     <property name="settings.editor.selected.configurable" value="flutter.settings" />
     <property name="show.migrate.to.gradle.popup" value="false" />
   </component>
   <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/ios/Assets/Images" />
+    </key>
     <key name="MoveFile.RECENT_KEYS">
       <recent name="$PROJECT_DIR$/ios" />
     </key>
@@ -92,7 +112,14 @@
       <option name="project" value="LOCAL" />
       <updated>1585371173535</updated>
     </task>
-    <option name="localTasksCounter" value="5" />
+    <task id="LOCAL-00005" summary="去除无用的输出">
+      <created>1585372093906</created>
+      <option name="number" value="00005" />
+      <option name="presentableId" value="LOCAL-00005" />
+      <option name="project" value="LOCAL" />
+      <updated>1585372093906</updated>
+    </task>
+    <option name="localTasksCounter" value="6" />
     <servers />
   </component>
   <component name="Vcs.Log.History.Properties">
@@ -121,19 +148,15 @@
   <component name="VcsManagerConfiguration">
     <MESSAGE value="更改IOS依赖包问题" />
     <MESSAGE value="解决IOS定位问题" />
-    <option name="LAST_COMMIT_MESSAGE" value="解决IOS定位问题" />
+    <MESSAGE value="去除无用的输出" />
+    <option name="LAST_COMMIT_MESSAGE" value="去除无用的输出" />
   </component>
   <component name="XDebuggerManager">
     <breakpoint-manager>
       <breakpoints>
         <line-breakpoint enabled="true" type="Dart">
           <url>file://$PROJECT_DIR$/lib/amap_location.dart</url>
-          <line>51</line>
-          <option name="timeStamp" value="3" />
-        </line-breakpoint>
-        <line-breakpoint enabled="true" type="Dart">
-          <url>file://$PROJECT_DIR$/lib/amap_location.dart</url>
-          <line>36</line>
+          <line>45</line>
           <option name="timeStamp" value="5" />
         </line-breakpoint>
       </breakpoints>

+ 1 - 0
amap_location.iml

@@ -11,6 +11,7 @@
       <excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
       <excludeFolder url="file://$MODULE_DIR$/example/.pub" />
       <excludeFolder url="file://$MODULE_DIR$/example/build" />
+      <excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
     </content>
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="Dart SDK" level="project" />

+ 16 - 0
example/ios/Runner.xcodeproj/project.pbxproj

@@ -162,6 +162,7 @@
 				9705A1C41CF9048500538489 /* Embed Frameworks */,
 				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
 				DE4388E0B9F914A340E7C81F /* [CP] Embed Pods Frameworks */,
+				7D20052C58D814336066BB01 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -234,6 +235,21 @@
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
 		};
+		7D20052C58D814336066BB01 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "[CP] Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 		7FAD61CFC10A47588D89E05F /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;

+ 2 - 0
example/ios/Runner/Info.plist

@@ -58,5 +58,7 @@
 	</array>
 	<key>UIViewControllerBasedStatusBarAppearance</key>
 	<false/>
+	<key>io.flutter.embedded_views_preview</key>
+    <true/>
 </dict>
 </plist>

+ 0 - 11
example/lib/map_util.dart

@@ -25,15 +25,4 @@ class MapUtil {
     await launch(url);
     return true;
   }
-
-  // apple地图
-  static Future<bool> goAppleMap(double lon, double lat) async {
-    var url = 'https://maps.apple.com/?sll=${lon.toString() + "," + lat.toString()}&t=s';
-    bool canLaunchUrl = await canLaunch(url);
-    if (!canLaunchUrl) {
-      return false;
-    }
-    await launch(url);
-    return true;
-  }
 }

+ 13 - 11
example/lib/map_view_page.dart

@@ -56,8 +56,8 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
   void initCityData() async {
     // kingWay 的环境
     var httpClient = new HttpClient();
-    HttpClientRequest request = await httpClient
-        .getUrl(Uri.http("47.103.219.158:31103", "/api/v1/school_map/all_addr"));
+    HttpClientRequest request = await httpClient.getUrl(
+        Uri.http("47.103.219.158:31103", "/api/v1/school_map/all_addr"));
     var response = await request.close();
     var responseBody = await response.transform(Utf8Decoder()).join();
     List<Map> data = jsonDecode(responseBody)['data'].cast<Map>();
@@ -82,7 +82,7 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
           builder: (context) {
             return Container(
               color: Colors.white,
-              height: Platform.isIOS ? 158 + 50 : 158,
+              height: 208,
               margin: EdgeInsets.only(
                 bottom: MediaQuery.of(context).padding.bottom,
               ),
@@ -108,7 +108,8 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
                                   ),
                                 ),
                               ),
-                              onTap: () => _goThirdPartMapApp(context, MapType.Apple, m),
+                              onTap: () =>
+                                  _goThirdPartMapApp(context, MapType.Apple, m),
                             ),
                           ),
                         ),
@@ -131,7 +132,8 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
                             ),
                           ),
                         ),
-                        onTap: () => _goThirdPartMapApp(context, MapType.AMap, m),
+                        onTap: () =>
+                            _goThirdPartMapApp(context, MapType.AMap, m),
                       ),
                     ),
                   ),
@@ -151,7 +153,8 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
                             ),
                           ),
                         ),
-                        onTap: () => _goThirdPartMapApp(context, MapType.Baidu, m),
+                        onTap: () =>
+                            _goThirdPartMapApp(context, MapType.Baidu, m),
                       ),
                     ),
                   ),
@@ -184,7 +187,8 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
     });
   }
 
-  void _goThirdPartMapApp(BuildContext context, MapType type, dynamic data) async {
+  void _goThirdPartMapApp(
+      BuildContext context, MapType type, dynamic data) async {
     // close bottom sheet
     Navigator.of(context).pop();
     bool success;
@@ -193,12 +197,10 @@ class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
     } else if (type == MapType.Baidu) {
       success = await MapUtil.goBaiduMap(data['lon'], data['lat']);
     } else if (type == MapType.Apple) {
-      success = await MapUtil.goAppleMap(data['lon'], data['lat']);
+      AmapLocation.instance.appleNavigate(data['lat'], data['lon'],data['title']);
     }
     print("jump ${success ? "successful" : "failed"}");
   }
 }
 
-enum MapType {
-  Apple, Baidu, AMap
-}
+enum MapType { Apple, Baidu, AMap }

BIN
ios/Assets/Images/dog.png


BIN
ios/Assets/Images/i2.png


+ 1 - 1
ios/Classes/AmapLocationPlugin.h

@@ -4,5 +4,5 @@
 
 @interface AmapLocationPlugin : NSObject<FlutterPlugin,AMapLocationManagerDelegate>
 
-@property (nonatomic, weak) FlutterMethodChannel* channel;
+
 @end

+ 32 - 13
ios/Classes/AmapLocationPlugin.m

@@ -1,20 +1,22 @@
 #import "AmapLocationPlugin.h"
-
+#import "FlutterAmapView.h"
+#import <MapKit/MapKit.h>
 @interface AmapLocationPlugin()<AMapLocationManagerDelegate> {
     AMapLocationManager* _aMapManager;
     FlutterMethodChannel* _flutterChannel;
+    FlutterAmapViewFactory* _amapViewFactory;
 }
 
 @end
 
 @implementation AmapLocationPlugin
 
--(instancetype)initWithBinaryMessager:(NSObject<FlutterBinaryMessenger>*)messenger{
+-(instancetype)initWithBinaryMessager:(NSObject<FlutterBinaryMessenger> *)messenger {
     if(self == [super init]){
         _flutterChannel = [FlutterMethodChannel
-        methodChannelWithName:@"amap_location"
-        binaryMessenger:messenger];
-        
+                           methodChannelWithName:@"amap_location"
+                           binaryMessenger:messenger];
+        _amapViewFactory = [[FlutterAmapViewFactory alloc]initWithMessenger:messenger];
     }
     return self;
 }
@@ -23,6 +25,7 @@
     AmapLocationPlugin* instance = [[AmapLocationPlugin alloc] initWithBinaryMessager:registrar.messenger];
     
     [registrar addMethodCallDelegate:instance channel:[instance channel]];
+    [registrar registerViewFactory:[instance viewFactory] withId:@"com.i2edu.mapView"];
     [registrar addApplicationDelegate:instance];
 }
 
@@ -30,6 +33,10 @@
     return _flutterChannel;
 }
 
+-(FlutterAmapViewFactory *)viewFactory{
+    return _amapViewFactory;
+}
+
 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
     if ([@"getPlatformVersion" isEqualToString:call.method]) {
         result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
@@ -37,6 +44,18 @@
         [self startLocation:call result:result];
     } else if([@"closeLocation" isEqualToString:call.method]){
         [self closeLocation:call result:result];
+    } else if([@"appleNavigate" isEqualToString:call.method]){
+        NSNumber* lat = [call.arguments objectForKey:@"lat"];
+        NSNumber* lng = [call.arguments objectForKey:@"lng"];
+        CLLocationCoordinate2D _currentLocationCoordinate = CLLocationCoordinate2DMake(lat.doubleValue, lng.doubleValue);
+        MKMapItem* currentLocation = [MKMapItem mapItemForCurrentLocation];
+        MKMapItem* toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:_currentLocationCoordinate addressDictionary:nil]];
+        toLocation.name = [call.arguments objectForKey:@"title"];
+        
+        [MKMapItem openMapsWithItems:@[currentLocation, toLocation]
+                       launchOptions:@{MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving,MKLaunchOptionsShowsTrafficKey: [NSNumber numberWithBool:YES]}];
+        result(nil);
+         
     }else {
         result(FlutterMethodNotImplemented);
     }
@@ -46,7 +65,7 @@
     NSNumber* isOnceLocation = call.arguments[@"isOnceLocation"];
     NSNumber* isNeedAddress = call.arguments[@"isNeedAddress"];
     NSNumber* mockEnable = call.arguments[@"mockEnable"];
-//    NSNumber* locationInterval = call.arguments[@"locationInterval"];
+    //    NSNumber* locationInterval = call.arguments[@"locationInterval"];
     NSNumber* locationTimeOut = call.arguments[@"locationTimeOut"];
     NSNumber* locationMode = call.arguments[@"locationMode"];
     
@@ -71,7 +90,7 @@
         NSLog(@"一次定位");
         [_aMapManager requestLocationWithReGeocode:isNeedAddress.boolValue completionBlock:^(CLLocation *location, AMapLocationReGeocode *regeocode, NSError *error) {
             [self handleResult:location regeocode:regeocode error:error];
-           
+            
         }];
     }else{
         [_aMapManager startUpdatingLocation];
@@ -98,23 +117,23 @@
             result[@"province"]=regeocode.province;
             result[@"city"]=regeocode.city;
             result[@"district"]=regeocode.district;
-//            result[@"road"]=regeocode.r;
+            //            result[@"road"]=regeocode.r;
             result[@"street"]=regeocode.street;
             result[@"number"]=regeocode.number;
             result[@"poiname"]=regeocode.POIName;
             result[@"errorCode"]=error==nil?nil:@(error.code);
             result[@"errorInfo"]=nil;
-//            result[@"locationType"]=location.;
-//            result[@"locationDetail"]=location.detail;
+            //            result[@"locationType"]=location.;
+            //            result[@"locationDetail"]=location.detail;
             result[@"aoiname"]=regeocode.AOIName;
             result[@"address"]=regeocode.formattedAddress;
-//            result[@"poiid"]=location.poi;
+            //            result[@"poiid"]=location.poi;
             result[@"floor"]=[[NSString alloc]initWithFormat:@"%ld",(long)location.floor.level];
             result[@"description"]=regeocode.description;
             result[@"time"]=@(location.timestamp.timeIntervalSinceNow);
             result[@"provider"]=regeocode.province;
-//            result[@"accuracy"]=location.accur;
-//            result[@"isOffset"]=location.;
+            //            result[@"accuracy"]=location.accur;
+            //            result[@"isOffset"]=location.;
             NSLog(@"reGeocode:%@", regeocode);
             [_flutterChannel invokeMethod:@"location" arguments:[self convertToJsonData:result]];
         }

+ 18 - 0
ios/Classes/CustomAnnotation.h

@@ -0,0 +1,18 @@
+//
+//  CustomAnnotation.h
+//  amap_location
+//
+//  Created by i2edu on 2020/4/23.
+//
+
+#import <Foundation/Foundation.h>
+#import <MAMapKit/MAMapKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CustomAnnotation : MAPointAnnotation
+@property (nonatomic, copy) NSString *phoneNumber; //商户名
+@property (nonatomic, copy) NSDictionary *map; //字典
+@end
+
+NS_ASSUME_NONNULL_END

+ 12 - 0
ios/Classes/CustomAnnotation.m

@@ -0,0 +1,12 @@
+//
+//  CustomAnnotation.m
+//  amap_location
+//
+//  Created by i2edu on 2020/4/23.
+//
+
+#import "CustomAnnotation.h"
+
+@implementation CustomAnnotation
+
+@end

+ 28 - 0
ios/Classes/CustomCalloutView.h

@@ -0,0 +1,28 @@
+//
+//  CustomCalloutView.h
+//  amap_location
+//
+//  Created by i2edu on 2020/4/23.
+//
+
+#import <Foundation/Foundation.h>
+#import <MAMapKit/MAMapKit.h>
+#import <Flutter/Flutter.h>
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CustomCalloutView : UIView
+
+
+@property (nonatomic, copy) NSString *title; //商户名
+@property (nonatomic, copy) NSString *subtitle; //地址
+@property (nonatomic, copy) NSString *phoneNumber; //电话
+@end
+
+@interface CustomAnnotationView : MAAnnotationView
+
+@property (nonatomic, strong, readwrite) CustomCalloutView *calloutView;
+@property (nonatomic, strong, readwrite) FlutterMethodChannel* channel;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 225 - 0
ios/Classes/CustomCalloutView.m

@@ -0,0 +1,225 @@
+//
+//  CustomCalloutView.m
+//  amap_location
+//
+//  Created by i2edu on 2020/4/23.
+//
+
+#import "CustomCalloutView.h"
+#import "CustomAnnotation.h"
+#define kArrorHeight        10
+#define kPortraitMargin     5
+
+#define kTitleWidth         190
+#define kTitleHeight        20
+
+#define kTitleSize          12
+#define kSubTitleSize       12
+#define kPhoneNumSize       12
+#define kGotoSize           12
+@interface CustomCalloutView ()
+
+
+@property (nonatomic, strong) UILabel *subtitleLabel;
+@property (nonatomic, strong) UILabel *titleLabel;
+@property (nonatomic, strong) UILabel *phoneLabel;
+@property (nonatomic, strong) UILabel *clickLabel;
+
+
+
+@end
+
+
+@implementation CustomCalloutView
+
+- (id)initWithFrame:(CGRect)frame
+{
+    self = [super initWithFrame:frame];
+    if (self)
+    {
+        self.backgroundColor = [UIColor clearColor];
+        [self initSubViews];
+    }
+    return self;
+}
+
+- (void)initSubViews
+{
+    // 添加标题,即商户名
+    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(kPortraitMargin * 2 , kPortraitMargin, kTitleWidth, kTitleHeight)];
+    self.titleLabel.font = [UIFont boldSystemFontOfSize:kTitleSize];
+    self.titleLabel.textColor = [UIColor blackColor];
+    self.titleLabel.text = _title;
+    self.titleLabel.numberOfLines=0;
+    [self addSubview:self.titleLabel];
+    
+    // 添加副标题,即商户地址
+    self.subtitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(kPortraitMargin * 2, kPortraitMargin * 2 + kTitleHeight, kTitleWidth, kTitleHeight)];
+    self.subtitleLabel.font = [UIFont systemFontOfSize:kSubTitleSize];
+    self.subtitleLabel.textColor = [UIColor grayColor];
+    self.subtitleLabel.text = [NSString stringWithFormat:@"地址:%@",_subtitle];
+    self.subtitleLabel.numberOfLines=0;
+    
+    [self addSubview:self.subtitleLabel];
+    
+    // 店家d电话
+    self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(kPortraitMargin * 2, kPortraitMargin * 2 + kTitleHeight*2, kTitleWidth, kTitleHeight)];
+    self.phoneLabel.font = [UIFont systemFontOfSize:kSubTitleSize];
+    self.phoneLabel.textColor = [UIColor grayColor];
+    self.phoneLabel.text = [NSString stringWithFormat:@"电话:%@",_phoneNumber];
+    self.phoneLabel.numberOfLines=0;
+    [self addSubview:self.phoneLabel];
+    
+    // 添加到这里去
+    self.clickLabel = [[UILabel alloc] initWithFrame:CGRectMake(kPortraitMargin * 2 , kPortraitMargin * 2 + kTitleHeight*3, kTitleWidth, kTitleHeight)];
+    self.clickLabel.font = [UIFont systemFontOfSize:12];
+    self.clickLabel.textColor = [UIColor blueColor];
+    NSDictionary *attribtDic = @{NSUnderlineStyleAttributeName: [NSNumber numberWithInteger:NSUnderlineStyleSingle]};
+    NSMutableAttributedString *attribtStr = [[NSMutableAttributedString alloc]initWithString:@"到这里去" attributes:attribtDic];
+    self.clickLabel.attributedText = attribtStr;
+    [self addSubview:self.clickLabel];
+    [self updatePosition];
+    
+}
+
+
+- (void)setTitle:(NSString *)title
+{
+    self.titleLabel.text = title;
+    [self updatePosition];
+}
+
+
+- (void)setSubtitle:(NSString *)subtitle
+{
+    self.subtitleLabel.text = [NSString stringWithFormat:@"地址:%@",subtitle];
+    [self updatePosition];
+}
+
+- (void)setPhoneNumber:(NSString *)phoneNumer
+{
+    self.phoneLabel.text = [NSString stringWithFormat:@"电话:%@",phoneNumer];
+    [self updatePosition];
+}
+
+- (void)updatePosition{
+    NSDictionary *attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:kTitleSize]};
+    CGSize titleSize = [self.titleLabel.text boundingRectWithSize:CGSizeMake(200, 5000) options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;
+    
+    self.titleLabel.frame = CGRectMake(self.titleLabel.frame.origin.x, self.titleLabel.frame.origin.y, self.titleLabel.frame.size.width, titleSize.height);
+    
+    CGSize subTitleSize = [self.subtitleLabel.text boundingRectWithSize:CGSizeMake(200, 5000) options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;
+    
+    self.subtitleLabel.frame = CGRectMake(self.subtitleLabel.frame.origin.x, self.titleLabel.frame.origin.y+self.titleLabel.frame.size.height+kPortraitMargin,self.subtitleLabel.frame.size.width, subTitleSize.height);
+    
+    self.phoneLabel.frame = CGRectMake(self.phoneLabel.frame.origin.x, self.subtitleLabel.frame.origin.y+self.subtitleLabel.frame.size.height+kPortraitMargin, self.phoneLabel.frame.size.width, self.phoneLabel.frame.size.height);
+    
+    self.clickLabel.frame = CGRectMake(self.clickLabel.frame.origin.x, self.phoneLabel.frame.origin.y+self.phoneLabel.frame.size.height+kPortraitMargin, self.clickLabel.frame.size.width, self.clickLabel.frame.size.height);
+    
+}
+
+
+- (void)drawRect:(CGRect)rect{
+    [self drawInContext:UIGraphicsGetCurrentContext()];
+    self.layer.shadowColor = [[UIColor whiteColor] CGColor];
+    self.layer.shadowOpacity = 1.0;
+    self.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
+}
+
+- (void)drawInContext:(CGContextRef)context
+{
+    
+    CGContextSetLineWidth(context, 2.0);
+    CGContextSetFillColorWithColor(context, [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8].CGColor);
+    [self getDrawPath:context];
+    CGContextFillPath(context);
+}
+- (void)getDrawPath:(CGContextRef)context
+{
+    CGRect rrect = self.bounds;
+    CGFloat radius = 6.0;
+    CGFloat minx = CGRectGetMinX(rrect),
+    midx = CGRectGetMidX(rrect),
+    maxx = CGRectGetMaxX(rrect);
+    CGFloat miny = CGRectGetMinY(rrect),
+    maxy = CGRectGetMaxY(rrect)-kArrorHeight;
+    
+    CGContextMoveToPoint(context, midx+kArrorHeight, maxy);
+    CGContextAddLineToPoint(context,midx, maxy+kArrorHeight);
+    CGContextAddLineToPoint(context,midx-kArrorHeight, maxy);
+    
+    CGContextAddArcToPoint(context, minx, maxy, minx, miny, radius);
+    CGContextAddArcToPoint(context, minx, minx, maxx, miny, radius);
+    CGContextAddArcToPoint(context, maxx, miny, maxx, maxx, radius);
+    CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
+    CGContextClosePath(context);
+}
+@end
+
+#define kCalloutWidth       220.0
+#define kCalloutHeight      150.0
+
+
+
+
+@implementation CustomAnnotationView
+
+- (void)setSelected:(BOOL)selected animated:(BOOL)animated
+{
+    if (self.selected == selected)
+    {
+        return;
+    }
+    
+    if (selected)
+    {
+        if (self.calloutView == nil)
+        {
+            self.calloutView = [[CustomCalloutView alloc] initWithFrame:CGRectMake(0, 0, kCalloutWidth, kCalloutHeight)];
+            self.calloutView.center = CGPointMake(CGRectGetWidth(self.bounds) / 2.f + self.calloutOffset.x,
+                                                  -CGRectGetHeight(self.calloutView.bounds) / 2.f + self.calloutOffset.y);
+        }
+        self.calloutView.title = self.annotation.title;
+        self.calloutView.subtitle = self.annotation.subtitle;
+        if ([self.annotation isKindOfClass:[CustomAnnotation class]])
+        {
+            CustomAnnotation*  myAnnotation= self.annotation;
+            self.calloutView.phoneNumber =myAnnotation.phoneNumber;
+            self.calloutView.phoneLabel.text =[NSString stringWithFormat:@"电话:%@",myAnnotation.phoneNumber];
+            self.calloutView.clickLabel.userInteractionEnabled=YES;
+            UITapGestureRecognizer *labelTapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(labelTouchUpInside:)];
+            [self.calloutView.clickLabel addGestureRecognizer:labelTapGestureRecognizer];
+            //            [self.calloutView.addGestureRecognizer:labelTapGestureRecognizer];
+        }
+        [self addSubview:self.calloutView];
+    }
+    else
+    {
+        [self.calloutView removeFromSuperview];
+    }
+    
+    [super setSelected:selected animated:animated];
+}
+-(void) labelTouchUpInside:(UITapGestureRecognizer *)recognizer{
+    //    UILabel *label=(UILabel*)recognizer.view;
+    //    NSLog(@"%@被点击了",label.text);
+    if ([self.annotation isKindOfClass:[CustomAnnotation class]])
+    {
+        CustomAnnotation*  myAnnotation= self.annotation;
+        [self.channel invokeMethod:@"guide" arguments:myAnnotation.map];
+    }
+}
+
+- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
+    UIView *view = [super hitTest:point withEvent:event];
+    if (view == nil) {
+        CGPoint tempoint = [self.calloutView.clickLabel convertPoint:point fromView:self];
+        if (CGRectContainsPoint(self.calloutView.clickLabel.bounds, tempoint)) {
+            view = self.calloutView.clickLabel;
+        }
+    }
+    return view;
+}
+
+@end
+

+ 27 - 0
ios/Classes/FlutterAmapView.h

@@ -0,0 +1,27 @@
+//
+//  AmapViewFactory.h
+//  amap_location
+//
+//  Created by i2edu on 2020/4/23.
+//
+
+#import <Foundation/Foundation.h>
+#import <Flutter/Flutter.h>
+#import <MAMapKit/MAMapKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FlutterAmapView : NSObject<FlutterPlatformView>
+
+-(instancetype _Nullable)initWithFrame:(CGRect)frame viewindentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject<FlutterBinaryMessenger> *_Nonnull)messenger;
+
+-(nonnull UIView*) view;
+
+@end
+
+@interface FlutterAmapViewFactory : NSObject<FlutterPlatformViewFactory>
+
+-(instancetype _Nullable )initWithMessenger:(NSObject<FlutterBinaryMessenger>*_Nonnull)messenger;
+
+@end
+NS_ASSUME_NONNULL_END

+ 136 - 0
ios/Classes/FlutterAmapView.m

@@ -0,0 +1,136 @@
+//
+//  AmapViewFactory.m
+//  amap_location
+//
+//  Created by i2edu on 2020/4/23.
+//
+
+#import "FlutterAmapView.h"
+#import "CustomCalloutView.h"
+#import "CustomAnnotation.h"
+static NSString* viewType = @"com.i2edu.mapView";
+
+
+@implementation FlutterAmapViewFactory{
+    NSObject<FlutterBinaryMessenger>* _messenger;
+    FlutterAmapView* amapView;
+    
+}
+
+- (nonnull NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args {
+    FlutterAmapView* amapView = [[FlutterAmapView alloc]initWithFrame:frame viewindentifier:viewId arguments:args binaryMessenger:_messenger];
+    return amapView;
+}
+- (NSObject<FlutterMessageCodec> *)createArgsCodec{
+    return [FlutterStandardMessageCodec sharedInstance];
+}
+
+- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger{
+    self = [super init];
+    if (self) {
+        _messenger=messenger;
+    }
+    return self;
+}
+@end
+
+@interface FlutterAmapView()<MAMapViewDelegate>
+@property(nonatomic ,strong) MAMapView* mapView;
+@property(nonatomic, strong)FlutterMethodChannel* channel;
+
+@end
+
+@implementation FlutterAmapView{
+    
+}
+- (instancetype)initWithFrame:(CGRect)frame viewindentifier:(int64_t)viewId arguments:(id)args binaryMessenger:(NSObject<FlutterBinaryMessenger> *)messenger{
+    if(self = [super init]){
+        NSString * channelName=[NSString stringWithFormat:@"com.i2edu.mapView/map_view_%lld",viewId];
+        _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger];
+        __weak __typeof__(self) weakSelf = self;
+        [weakSelf.channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
+            [weakSelf onMethodCall:call result:result];
+        }];
+        _mapView = [[MAMapView alloc] initWithFrame:frame];
+        _mapView.backgroundColor = [UIColor clearColor];
+        _mapView.frame = frame;
+        [_mapView updateUserLocationRepresentation:[self getRepresentation]];
+        _mapView.delegate=self;
+        
+        ///如果您需要进入地图就显示定位小蓝点,则需要下面两行代码
+        _mapView.showsUserLocation = YES;
+        _mapView.userTrackingMode = MAUserTrackingModeFollow;
+    }
+    return self;
+    
+}
+
+-(MAUserLocationRepresentation*)getRepresentation{
+    MAUserLocationRepresentation *r = [[MAUserLocationRepresentation alloc] init];
+    r.image=[UIImage imageNamed:@"dog"];
+//    r.image=[UIImage imageWithContentsOfFile:@"Assets/Images/dog.png"];
+//    r.locationDotFillColor = [UIColor redColor];
+    return r;
+    
+}
+
+- (UIView *)view{
+    return _mapView;
+    
+}
+
+-(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
+    if ([call.method isEqualToString:@"setMarkers"]) {
+        NSArray<NSDictionary*> * markers = [call.arguments objectForKey:@"markers"];
+        for (NSDictionary* marker in markers) {
+            CustomAnnotation *pointAnnotation = [[CustomAnnotation alloc] init];
+            NSNumber* lat = marker[@"lat"];
+            NSNumber* lon = marker[@"lon"];
+            
+            CLLocationDegrees latitude = lat.doubleValue;
+            CLLocationDegrees longitude = lon.doubleValue;
+            pointAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
+            pointAnnotation.phoneNumber = marker[@"tel"];
+            pointAnnotation.map = marker;
+            
+            pointAnnotation.title = marker[@"title"];
+            pointAnnotation.subtitle = marker[@"content"];
+            
+            [_mapView addAnnotation:pointAnnotation];
+        }
+        result(nil);
+    }else if([call.method isEqualToString:@"onCreate"]){
+        result(nil);
+    }else if([call.method isEqualToString:@"onPause"]){
+        result(nil);
+    }else if([call.method isEqualToString:@"onResume"]){
+        result(nil);
+    }else {
+        result(FlutterMethodNotImplemented);
+    }
+}
+
+- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation
+{
+    
+    if ([annotation isKindOfClass:[CustomAnnotation class]])
+    {
+        static NSString *pointReuseIndentifier = @"pointReuseIndentifier";
+        
+        CustomAnnotationView* annotationView = (CustomAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndentifier];
+        if (annotationView == nil)
+        {
+            annotationView = [[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndentifier];
+        }
+        annotationView.channel = self.channel;
+        
+        annotationView.image = [UIImage imageNamed:@"i2"];
+//        annotationView.pinColor = MAPinAnnotationColorPurple;
+        annotationView.canShowCallout= NO;       //设置气泡可以弹出,默认为NO
+//        annotationView.animatesDrop = YES;        //设置标注动画显示,默认为NO
+//        annotationView.draggable = YES;        //设置标注可以拖动,默认为NO
+        return annotationView;
+    }
+    return nil;
+}
+@end

+ 2 - 0
ios/amap_location.podspec

@@ -15,8 +15,10 @@ A new Flutter plugin.
   s.source           = { :path => '.' }
   s.source_files = 'Classes/**/*'
   s.public_header_files = 'Classes/**/*.h'
+  s.resource = 'Assets/Images/*.png'
   s.dependency 'Flutter'
   s.dependency 'AMapLocation', '~>2.6.4'
+  s.dependency 'AMap3DMap'
   s.platform = :ios, '8.0'
   s.static_framework = true
   # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.

+ 8 - 1
lib/amap_location.dart

@@ -22,7 +22,6 @@ class AmapLocation {
   Stream<LocationResultEntity> get locationStream =>
       _locationStreamController.stream;
 
-
   AmapLocation() {
     _channel.setMethodCallHandler(platformCallHandler);
     _locationStreamController =
@@ -62,4 +61,12 @@ class AmapLocation {
     }
     return null;
   }
+
+  Future<void> appleNavigate(double lat, double lng,String title) async {
+    return await _channel.invokeMethod('appleNavigate', {
+      'lat': lat,
+      "lng": lng,
+      "title": title,
+    });
+  }
 }

+ 24 - 24
pubspec.lock

@@ -5,56 +5,56 @@ packages:
     dependency: transitive
     description:
       name: archive
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.11"
   args:
     dependency: transitive
     description:
       name: args
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.5.2"
   async:
     dependency: transitive
     description:
       name: async
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.4.0"
   boolean_selector:
     dependency: transitive
     description:
       name: boolean_selector
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.5"
   charcode:
     dependency: transitive
     description:
       name: charcode
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.2"
   collection:
     dependency: transitive
     description:
       name: collection
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.14.11"
   convert:
     dependency: transitive
     description:
       name: convert
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.1"
   crypto:
     dependency: transitive
     description:
       name: crypto
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.3"
   flutter:
@@ -71,49 +71,49 @@ packages:
     dependency: transitive
     description:
       name: image
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.4"
   matcher:
     dependency: transitive
     description:
       name: matcher
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.12.6"
   meta:
     dependency: transitive
     description:
       name: meta
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.8"
   path:
     dependency: transitive
     description:
       name: path
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.6.4"
   pedantic:
     dependency: transitive
     description:
       name: pedantic
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.8.0+1"
   petitparser:
     dependency: transitive
     description:
       name: petitparser
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.4.0"
   quiver:
     dependency: transitive
     description:
       name: quiver
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.5"
   sky_engine:
@@ -125,63 +125,63 @@ packages:
     dependency: transitive
     description:
       name: source_span
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.5.5"
   stack_trace:
     dependency: transitive
     description:
       name: stack_trace
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.9.3"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.0"
   string_scanner:
     dependency: transitive
     description:
       name: string_scanner
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.5"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.0"
   test_api:
     dependency: transitive
     description:
       name: test_api
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.11"
   typed_data:
     dependency: transitive
     description:
       name: typed_data
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.6"
   vector_math:
     dependency: transitive
     description:
       name: vector_math
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.8"
   xml:
     dependency: transitive
     description:
       name: xml
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.5.0"
 sdks: