68 Commits

Author SHA1 Message Date
eae654c47e fix 2026-04-14 14:04:19 +08:00
f01875abb9 调整 2026-04-14 11:50:33 +08:00
881da166d1 调整 2026-04-13 17:48:37 +08:00
14b2e6b35c Merge branch 'dev_map' into dev 2026-04-13 16:54:07 +08:00
20c39a4a12 样式调整 2026-04-13 16:38:46 +08:00
xiaogg
5c74c7ccc0 fix:优化完善; 2026-04-13 16:13:23 +08:00
23fc0da5c8 新增字段 2026-04-13 15:05:29 +08:00
7efd933416 新增字段 2026-04-09 14:05:27 +08:00
2b33dac384 语音 2026-04-08 10:59:43 +08:00
ef60b8ed62 Merge branch 'map_dev' into dev 2026-04-03 10:25:53 +08:00
bec6952707 Merge branch 'dev_map' into map_dev 2026-04-02 09:11:14 +08:00
f68349a208 Merge branch 'dev_map' into dev 2026-04-01 17:38:48 +08:00
xiaogg
08dcc64ef4 fix:优化完善; 2026-04-01 15:12:54 +08:00
9242dcaf70 优化 2026-04-01 15:06:04 +08:00
95fdfe6269 增加演示账号 2026-04-01 11:41:44 +08:00
ee88b3ada9 优化 2026-04-01 11:34:32 +08:00
ba27799c41 显示问题 2026-03-31 17:12:00 +08:00
c527732532 Merge branch 'main' into dev
# Conflicts:
#	ln_jq_app/android/app/src/main/AndroidManifest.xml
#	ln_jq_app/pubspec.lock
同步线上
2026-03-31 15:07:16 +08:00
cd223fa9bf Merge branch 'map_dev' into dev
# Conflicts:
#	ln_jq_app/android/app/src/main/AndroidManifest.xml
#	ln_jq_app/ios/Runner/Info.plist
2026-03-31 13:49:42 +08:00
fdd6653fce Merge branch 'dev_map' into map_dev
# Conflicts:
#	ln_jq_app/ios/Podfile.lock
#	ln_jq_app/lib/pages/c_page/base_widgets/view.dart
地图导航相关
2026-03-31 10:07:43 +08:00
8748a60f7f 优化算路 2026-03-30 16:13:22 +08:00
xiaogg
5843ef6e87 feat:增加途经点,完善规划,导航; 2026-03-27 15:39:36 +08:00
0123025296 优化搜索 2026-03-27 13:23:10 +08:00
587dd48896 调整地址输入 2026-03-27 11:45:07 +08:00
569780ffb4 样式 2026-03-27 09:37:52 +08:00
3aac4e7330 调整地图选点 2026-03-26 18:01:05 +08:00
b275c66383 ui 2026-03-26 16:58:57 +08:00
bf80931f37 bugfix 2026-03-25 17:56:37 +08:00
67c36f436c bugfix 2026-03-25 17:18:06 +08:00
c10c92afdc 增加token 2026-03-25 11:02:15 +08:00
d3edf95df3 单独缓存车牌 2026-03-24 09:38:16 +08:00
930953780d 路径接口 2026-03-20 10:23:45 +08:00
a497bc6469 清理插件 2026-03-19 17:41:28 +08:00
3f282d15c1 协议修改 2026-03-19 16:37:57 +08:00
03b35f660c 协议前置 2026-03-19 14:41:32 +08:00
9b6f93ca95 隐私调整 2026-03-17 13:31:58 +08:00
eb41ecaec4 更新插件 2026-03-16 16:00:38 +08:00
384de27f2c Merge branch 'dev' 2026-03-13 17:18:43 +08:00
84b174c4a5 协议通知后注册推送 2026-03-13 17:09:30 +08:00
cd14469d79 调整定位 2026-03-12 13:27:08 +08:00
b846d352a2 android 地图 2026-03-10 17:31:27 +08:00
xiaogg
a24f41a8d5 fix:添加本地库 2026-03-09 09:38:22 +08:00
xiaogg
65b4a3ac34 接入地图SDK 2026-03-09 08:53:34 +08:00
02e1946319 文字边界 2026-03-05 11:20:14 +08:00
d5083c1939 Merge branch 'dev'
v1.2.4
2026-03-04 14:58:45 +08:00
fe44848529 号码 2026-03-04 14:50:21 +08:00
572c416827 枪号回填,v1.2.4 2026-03-04 13:38:26 +08:00
8df16f0787 pdf查看 2026-03-03 17:51:06 +08:00
ce6bd3edd2 bugfix 2026-03-03 13:09:10 +08:00
6997b4ac9e 调整权限和库 2026-03-02 11:28:44 +08:00
a26d2478f3 样式 2026-02-28 17:31:28 +08:00
0dded3b928 ocr识别,历史新增类型 2026-02-28 17:15:42 +08:00
b7caf58adf 加氢站-查看证件 2026-02-28 15:00:56 +08:00
0df1902df2 无预约 2026-02-28 11:59:07 +08:00
a8314d8a7a 关闭弹窗 2026-02-27 10:55:55 +08:00
39cae906e9 加氢站-预约 2026-02-27 10:54:55 +08:00
14fd6c75d0 fix 2026-02-25 15:35:33 +08:00
1724852a39 司机-可上传证件 2026-02-25 15:35:02 +08:00
a05d4ebb9b 调整 2026-02-12 18:01:56 +08:00
600cea4379 进度条 2026-02-12 17:34:06 +08:00
3dfc1dfc2c 样式调整 2026-02-12 16:54:50 +08:00
909dc95771 增加 提示 2026-02-11 17:41:30 +08:00
cf0896453b 样式 2026-02-11 11:28:49 +08:00
35bd3a78a5 Merge branch 'dev' 2026-01-30 17:33:15 +08:00
032e60d362 Merge branch 'dev'
ui调整
# Conflicts:
#	ln_jq_app/lib/pages/login/view.dart
2026-01-30 17:08:49 +08:00
45f5035d1b 更换logo 2026-01-19 10:13:37 +08:00
f792915429 bugfix 2026-01-16 17:47:53 +08:00
edbacc502b 线上域名修改 2026-01-16 17:25:52 +08:00
169 changed files with 10493 additions and 586 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(flutter build apk --debug 2>&1 | tail -15)",
"Bash(flutter build apk --debug 2>&1 | tail -10)"
]
}
}

View File

@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(./gradlew assembleDebug --stacktrace)"
]
}
}

View File

@@ -37,8 +37,8 @@ android {
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = 6
versionName = "1.2.3"
versionCode = 7
versionName = "1.2.4"
}
signingConfigs {
@@ -68,3 +68,9 @@ android {
flutter {
source = "../.."
}
dependencies {
implementation("com.amap.api:navi-3dmap-location-search:10.0.700_3dmap10.0.700_loc6.4.5_sea9.7.2")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("androidx.appcompat:appcompat:1.7.1")
}

View File

@@ -59,3 +59,30 @@
-dontwarn anetwork.**
-dontwarn com.ut.**
-dontwarn com.ta.**
#3D 地图 V5.0.0之前:
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.amap.mapcore.*{*;}
-keep class com.amap.api.trace.**{*;}
#3D 地图 V5.0.0之后:
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.**{*;}
-keep class com.amap.api.trace.**{*;}
#定位
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}
#搜索
-keep class com.amap.api.services.**{*;}
#2D地图
-keep class com.amap.api.maps2d.**{*;}
-keep class com.amap.api.mapcore2d.**{*;}
#导航
-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}

View File

@@ -1,47 +1,81 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!--定位权限-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--用于申请调用A-GPS模块-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission>
<!--如果设置了target >= 28 如果需要启动后台定位则必须声明这个权限-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!--如果您的应用需要后台定位权限且有可能运行在Android Q设备上,并且设置了target>28必须增加这个权限声明-->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:label="小羚羚"
android:requestLegacyExternalStorage="true"
android:name="${applicationName}"
android:icon="@mipmap/logo">
android:icon="@mipmap/logo"
android:label="小羚羚">
<!-- 高德地图Key -->
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="92495660f7bc990cb475426c47c03b65" />
<!-- 高德地图定位服务 -->
<service android:name="com.amap.api.location.APSService" />
<!--高德导航-->
<activity
android:name="com.amap.api.navi.AmapRouteActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation" />
<!--自定义导航 Activity-->
<activity
android:name="com.lnkj.ln_jq_app.NavigationActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
android:screenOrientation="portrait" />
<!--搜索目的地 Activity-->
<activity
android:name="com.lnkj.ln_jq_app.SearchDestinationActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
@@ -51,30 +85,13 @@
android:value="2" />
<!-- 请填写你自己的- appKey -->
<meta-data android:name="com.alibaba.app.appkey" android:value="335642645"/>
<meta-data
android:name="com.alibaba.app.appkey"
android:value="335642645" />
<!-- 请填写你自己的appSecret -->
<meta-data android:name="com.alibaba.app.appsecret" android:value="39628204345a4240b5b645b68a5896c7"/>
<!-- 华为通道的参数appid -->
<meta-data android:name="com.huawei.hms.client.appid" android:value="" />
<!-- vivo通道的参数api_key为appkey -->
<meta-data android:name="com.vivo.push.api_key" android:value="" />
<meta-data android:name="com.vivo.push.app_id" android:value="" />
<!-- honor通道的参数-->
<meta-data android:name="com.hihonor.push.app_id" android:value="" />
<!-- oppo -->
<meta-data android:name="com.oppo.push.key" android:value="" />
<meta-data android:name="com.oppo.push.secret" android:value="" />
<!-- 小米-->
<meta-data android:name="com.xiaomi.push.id" android:value="id=2222222222222222222" />
<meta-data android:name="com.xiaomi.push.key" android:value="id=5555555555555" />
<!-- 魅族-->
<meta-data android:name="com.meizu.push.id" android:value="" />
<meta-data android:name="com.meizu.push.key" android:value="" />
<meta-data
android:name="com.alibaba.app.appsecret"
android:value="39628204345a4240b5b645b68a5896c7" />
@@ -99,6 +116,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@@ -117,8 +135,8 @@
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
</manifest>

View File

@@ -1,6 +1,161 @@
package com.lnkj.ln_jq_app;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.lnkj.ln_jq_app/map";
private static final String TAG = "MainActivity";
// 权限请求码
private static final int PERMISSION_REQUEST_CODE = 1001;
private NativeMapView mapView;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
// 注册高德地图导航Platform View
flutterEngine
.getPlatformViewsController()
.getRegistry()
.registerViewFactory(
"NativeFirstPage",
new NativeMapFactory(this)
);
// 设置 Activity 实例到地图
NativeMapFactory.setActivity(this);
}
/**
* 获取当前系统版本需要申请的权限列表
*/
private String[] getRequiredPermissions() {
List<String> permissions = new ArrayList<>();
// 定位权限是必须的
permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
// 存储权限处理Android 13 (API 33) 以下才需要申请 legacy 存储权限
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
return permissions.toArray(new String[0]);
}
/**
* 检查并申请权限
*/
private void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String[] requiredPermissions = getRequiredPermissions();
List<String> deniedPermissions = new ArrayList<>();
for (String permission : requiredPermissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
deniedPermissions.add(permission);
}
}
if (!deniedPermissions.isEmpty()) {
ActivityCompat.requestPermissions(
this,
deniedPermissions.toArray(new String[0]),
PERMISSION_REQUEST_CODE
);
} else {
Log.d(TAG, "所有必要权限已授予");
if (mapView != null) {
mapView.startLocation();
}
}
} else {
if (mapView != null) {
mapView.startLocation();
}
}
}
private void requestPermissions() {
checkAndRequestPermissions();
}
public void setMapView(NativeMapView mapView) {
this.mapView = mapView;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mapView != null) {
mapView.dispose();
mapView = null;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 处理从搜索目的地页面返回的结果
if (requestCode == 1000 && resultCode == RESULT_OK && data != null) {
String name = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_NAME);
String address = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_ADDRESS);
double lat = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LAT, 0);
double lon = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LON, 0);
String district = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_DISTRICT);
if (name != null && lat != 0 && lon != 0) {
// 获取地图实例并设置目的地
NativeMapView mapView = NativeMapFactory.getMapView();
if (mapView != null) {
mapView.setDestination(name, address, lat, lon, district);
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean locationGranted = false;
for (int i = 0; i < permissions.length; i++) {
if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i])
&& grantResults[i] == PackageManager.PERMISSION_GRANTED) {
locationGranted = true;
break;
}
}
if (locationGranted) {
if (mapView != null) {
mapView.startLocation();
}
} else {
// 只有在定位权限确实被拒绝时才弹出提示
Toast.makeText(this, "请授予应用定位权限以正常使用地图功能", Toast.LENGTH_LONG).show();
}
}
}
}

View File

@@ -0,0 +1,47 @@
package com.lnkj.ln_jq_app;
import android.app.Activity;
import android.content.Context;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
/**
* 高德地图导航 Platform View Factory
* 对应iOS的NativeViewFactory
*/
public class NativeMapFactory extends PlatformViewFactory {
private static final String VIEW_TYPE_ID = "NativeFirstPage";
private static NativeMapView mapViewInstance = null;
private final Context context;
public NativeMapFactory(Context context) {
super(StandardMessageCodec.INSTANCE);
this.context = context;
}
@Override
public PlatformView create(Context context, int viewId, Object args) {
mapViewInstance = new NativeMapView(context, viewId, args);
return mapViewInstance;
}
/**
* 获取地图实例供MainActivity使用
*/
public static NativeMapView getMapView() {
return mapViewInstance;
}
/**
* 设置 Activity 实例
*/
public static void setActivity(Activity activity) {
if (mapViewInstance != null) {
mapViewInstance.setActivity(activity);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,408 @@
package com.lnkj.ln_jq_app;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.amap.api.maps.AMapException;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AMapNaviListener;
import com.amap.api.navi.AMapNaviView;
import com.amap.api.navi.AMapNaviViewListener;
import com.amap.api.navi.AMapNaviViewOptions;
import com.amap.api.navi.enums.NaviType;
import com.amap.api.navi.enums.PathPlanningStrategy;
import com.amap.api.navi.model.AMapCalcRouteResult;
import com.amap.api.navi.model.AMapCarInfo;
import com.amap.api.navi.model.AMapLaneInfo;
import com.amap.api.navi.model.AMapModelCross;
import com.amap.api.navi.model.AMapNaviCameraInfo;
import com.amap.api.navi.model.AMapNaviCross;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.AMapNaviRouteNotifyData;
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo;
import com.amap.api.navi.model.AMapServiceAreaInfo;
import com.amap.api.navi.model.AimLessModeCongestionInfo;
import com.amap.api.navi.model.AimLessModeStat;
import com.amap.api.navi.model.NaviInfo;
import com.amap.api.navi.model.NaviPoi;
import java.util.List;
import androidx.activity.ComponentActivity;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
/**
* 导航
*/
public class NavigationActivity extends ComponentActivity implements AMapNaviListener {
private static final String TAG = "NavigationActivity";
private AMapNavi mAMapNavi;
private AMapNaviView naviView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建导航视图
naviView = new AMapNaviView(this);
naviView.onCreate(savedInstanceState);
naviView.setAMapNaviViewListener(new AMapNaviViewListener() {
@Override
public void onNaviSetting() {
}
@Override
public void onNaviCancel() {
finish();
}
@Override
public boolean onNaviBackClick() {
return false;
}
@Override
public void onNaviMapMode(int i) {
}
@Override
public void onNaviTurnClick() {
}
@Override
public void onNextRoadClick() {
}
@Override
public void onScanViewButtonClick() {
}
@Override
public void onLockMap(boolean b) {
}
@Override
public void onNaviViewLoaded() {
}
@Override
public void onMapTypeChanged(int i) {
}
@Override
public void onNaviViewShowMode(int i) {
}
});
AMapNaviViewOptions aMapNaviViewOptions = new AMapNaviViewOptions();
Bitmap carBitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.car);
aMapNaviViewOptions.setCarBitmap(carBitmap);
aMapNaviViewOptions.setSettingMenuEnabled(false);
naviView.setViewOptions(aMapNaviViewOptions);
// 创建 AMapNavi 实例
try {
mAMapNavi = AMapNavi.getInstance(this);
mAMapNavi.addAMapNaviListener(this);
} catch (AMapException e) {
Log.e(TAG, "初始化导航失败", e);
Toast.makeText(this, "初始化导航失败", Toast.LENGTH_SHORT).show();
finish();
return;
}
// 添加到容器
FrameLayout container = new FrameLayout(this);
container.addView(naviView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(container);
// 获取 Intent 中的参数
processIntent(getIntent());
// 处理返回键
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
finish();
}
});
Log.d(TAG, "NavigationActivity created");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processIntent(intent);
}
/**
* 处理 Intent 参数并启动导航
*/
@SuppressWarnings("unchecked")
private void processIntent(Intent intent) {
if (intent == null) {
Log.w(TAG, "Intent is null");
return;
}
try {
// 获取起点、终点和途经点
NaviPoi startNaviPoi = (NaviPoi) intent.getParcelableExtra("startPoi");
NaviPoi endNaviPoi = (NaviPoi) intent.getParcelableExtra("endPoi");
List<NaviPoi> wayPoints = intent.getParcelableArrayListExtra("wayPoints");
if (wayPoints != null) {
Log.d("Navi", "获取到途径点数量: " + wayPoints.size());
}
if (startNaviPoi == null || endNaviPoi == null) {
Log.e(TAG, "Missing start or end point");
return;
}
// 设置车辆信息
AMapCarInfo carInfo = (AMapCarInfo) intent.getParcelableExtra("carInfo");
if (carInfo != null) {
mAMapNavi.setCarInfo(carInfo);
}
mAMapNavi.setUseInnerVoice(true);
// 计算并启动导航
mAMapNavi.calculateDriveRoute(startNaviPoi, endNaviPoi, wayPoints, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
Log.d(TAG, "Navigation started from: " + startNaviPoi.getName() + " - " + endNaviPoi.getName());
} catch (Exception e) {
Log.e(TAG, "Process intent error", e);
Toast.makeText(this, "导航启动失败", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onResume() {
super.onResume();
if (naviView != null) {
naviView.onResume();
}
}
@Override
protected void onPause() {
super.onPause();
if (naviView != null) {
naviView.onPause();
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (naviView != null) {
naviView.onSaveInstanceState(outState);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 停止导航
if (mAMapNavi != null) {
mAMapNavi.stopNavi();
mAMapNavi.removeAMapNaviListener(this);
}
if (naviView != null) {
naviView.onDestroy();
}
Log.d(TAG, "NavigationActivity destroyed");
}
private int dp2px(float dp) {
float density = getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5f);
}
// ============ AMapNaviListener 回调实现 ============
@Override
public void onInitNaviFailure() {
Log.e(TAG, "onInitNaviFailure");
}
@Override
public void onInitNaviSuccess() {
Log.d(TAG, "onInitNaviSuccess");
}
@Override
public void onStartNavi(int i) {
Log.d(TAG, "onStartNavi: " + i);
}
@Override
public void onTrafficStatusUpdate() {
}
@Override
public void onLocationChange(AMapNaviLocation aMapNaviLocation) {
}
@Override
public void onGetNavigationText(int i, String s) {
}
@Override
public void onGetNavigationText(String s) {
}
@Override
public void onEndEmulatorNavi() {
}
@Override
public void onArriveDestination() {
Log.d(TAG, "onArriveDestination");
}
@Override
public void onCalculateRouteFailure(int i) {
Log.e(TAG, "onCalculateRouteFailure: " + i);
Toast.makeText(this, "路径计算失败: " + i, Toast.LENGTH_SHORT).show();
}
@Override
public void updateCameraInfo(AMapNaviCameraInfo[] aMapNaviCameraInfos) {
}
@Override
public void updateIntervalCameraInfo(AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i) {
}
@Override
public void onServiceAreaUpdate(AMapServiceAreaInfo[] aMapServiceAreaInfos) {
}
@Override
public void showCross(AMapNaviCross aMapNaviCross) {
}
@Override
public void hideCross() {
}
@Override
public void showModeCross(AMapModelCross aMapModelCross) {
}
@Override
public void hideModeCross() {
}
@Override
public void showLaneInfo(AMapLaneInfo[] aMapLaneInfos, byte[] bytes, byte[] bytes1) {
}
@Override
public void showLaneInfo(AMapLaneInfo aMapLaneInfo) {
}
@Override
public void hideLaneInfo() {
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
Log.d(TAG, "onCalculateRouteSuccess");
if (mAMapNavi != null) {
mAMapNavi.startNavi(NaviType.GPS);
}
}
@Override
public void notifyParallelRoad(int i) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo) {
}
@Override
public void updateAimlessModeStatistics(AimLessModeStat aimLessModeStat) {
}
@Override
public void updateAimlessModeCongestionInfo(AimLessModeCongestionInfo aimLessModeCongestionInfo) {
}
@Override
public void onPlayRing(int i) {
}
@Override
public void onCalculateRouteSuccess(AMapCalcRouteResult aMapCalcRouteResult) {
Log.d(TAG, "onCalculateRouteSuccess (result)");
if (mAMapNavi != null) {
mAMapNavi.startNavi(NaviType.GPS);
}
}
@Override
public void onCalculateRouteFailure(AMapCalcRouteResult aMapCalcRouteResult) {
Log.e(TAG, "onCalculateRouteFailure: " + aMapCalcRouteResult.getErrorCode());
Toast.makeText(this, "路径计算失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onNaviRouteNotify(AMapNaviRouteNotifyData aMapNaviRouteNotifyData) {
}
@Override
public void onGpsSignalWeak(boolean b) {
}
@Override
public void onReCalculateRouteForYaw() {
Log.d(TAG, "onReCalculateRouteForYaw");
}
@Override
public void onReCalculateRouteForTrafficJam() {
}
@Override
public void onArrivedWayPoint(int i) {
}
@Override
public void onGpsOpenStatus(boolean b) {
}
@Override
public void onNaviInfoUpdate(NaviInfo naviInfo) {
}
}

View File

@@ -0,0 +1,261 @@
package com.lnkj.ln_jq_app;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.amap.api.services.help.Inputtips;
import com.amap.api.services.help.InputtipsQuery;
import com.amap.api.services.help.Tip;
import java.util.ArrayList;
import java.util.List;
import androidx.activity.ComponentActivity;
/**
* 搜索目的地页面
*/
public class SearchDestinationActivity extends ComponentActivity implements Inputtips.InputtipsListener {
private static final String TAG = "SearchDestinationActivity";
public static final String EXTRA_RESULT_NAME = "result_name";
public static final String EXTRA_RESULT_ADDRESS = "result_address";
public static final String EXTRA_RESULT_LAT = "result_lat";
public static final String EXTRA_RESULT_LON = "result_lon";
public static final String EXTRA_RESULT_DISTRICT = "result_district";
// 静态回调接口
public interface DestinationCallback {
void onDestinationSelected(String name, String address, double lat, double lon, String district);
}
private static DestinationCallback callback;
public static void setCallback(DestinationCallback callback) {
SearchDestinationActivity.callback = callback;
}
private EditText searchInput;
private ListView suggestionList;
private ImageView backBtn;
private ArrayAdapter<String> suggestionAdapter;
private List<Tip> currentTipList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
setContentView(R.layout.activity_search_destination);
initViews();
setupListeners();
// 自动显示键盘
showKeyboard();
} catch (Exception e) {
Log.e(TAG, "Error in onCreate", e);
e.printStackTrace();
finish();
}
}
private void initViews() {
try {
Log.d(TAG, "initViews started");
searchInput = findViewById(R.id.search_input);
suggestionList = findViewById(R.id.suggestion_list);
backBtn = findViewById(R.id.back_btn);
currentTipList = new ArrayList<>();
Log.d(TAG, "initViews completed: searchInput=" + (searchInput != null ? "ok" : "null") + ", suggestionList=" + (suggestionList != null ? "ok" : "null") + ", backBtn=" + (backBtn != null ? "ok" : "null"));
} catch (Exception e) {
Log.e(TAG, "Error in initViews", e);
e.printStackTrace();
}
suggestionAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<>()) {
@Override
public View getView(int position, android.view.View convertView, android.view.ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView text = view.findViewById(android.R.id.text1);
text.setTextColor(Color.parseColor("#333333"));
text.setTextSize(14);
view.setBackgroundColor(Color.TRANSPARENT);
return view;
}
};
suggestionList.setAdapter(suggestionAdapter);
suggestionList.setDivider(new ColorDrawable(Color.LTGRAY));
suggestionList.setDividerHeight(1);
}
private void setupListeners() {
try {
Log.d(TAG, "setupListeners started");
// 返回按钮
backBtn.setOnClickListener(v -> {
Log.d(TAG, "Back button clicked");
hideKeyboard();
finish();
});
// 文本变化监听
searchInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d(TAG, "Text changed: " + (s != null ? s.toString() : "null") + ", length=" + (s != null ? s.length() : 0));
if (s.length() > 1) {
searchLocation(s.toString());
} else {
suggestionList.setVisibility(View.GONE);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
// 列表点击
suggestionList.setOnItemClickListener((parent, view, position, id) -> {
Log.d(TAG, "Suggestion clicked at position: " + position);
try {
Tip tip = currentTipList.get(position);
if (tip.getPoint() != null) {
String name = tip.getName();
String address = tip.getAddress();
String district = tip.getDistrict();
double lat = tip.getPoint().getLatitude();
double lon = tip.getPoint().getLongitude();
Log.d(TAG, "Selected destination: " + name + ", lat=" + lat + ", lon=" + lon);
// 优先使用静态回调
if (callback != null) {
callback.onDestinationSelected(name, address, lat, lon, district);
} else {
// 降级使用 Intent 回调
Intent result = new Intent();
result.putExtra(EXTRA_RESULT_NAME, name);
result.putExtra(EXTRA_RESULT_ADDRESS, address);
result.putExtra(EXTRA_RESULT_LAT, lat);
result.putExtra(EXTRA_RESULT_LON, lon);
result.putExtra(EXTRA_RESULT_DISTRICT, district);
setResult(RESULT_OK, result);
}
hideKeyboard();
finish();
}
} catch (Exception e) {
Log.e(TAG, "Error handling suggestion click", e);
e.printStackTrace();
}
});
Log.d(TAG, "setupListeners completed");
} catch (Exception e) {
Log.e(TAG, "Error in setupListeners", e);
e.printStackTrace();
}
}
private void searchLocation(String keyword) {
try {
Log.d(TAG, "Searching for: " + keyword);
InputtipsQuery query = new InputtipsQuery(keyword, "");
query.setCityLimit(false);
Inputtips inputtips = new Inputtips(this, query);
inputtips.setInputtipsListener(this);
inputtips.requestInputtipsAsyn();
} catch (Exception e) {
Log.e(TAG, "Error in searchLocation", e);
e.printStackTrace();
}
}
@Override
public void onGetInputtips(List<Tip> tipList, int rCode) {
Log.d(TAG, "onGetInputtips called, rCode=" + rCode + ", tipList size=" + (tipList != null ? tipList.size() : "null"));
if (rCode == 1000 && tipList != null && !tipList.isEmpty()) {
currentTipList.clear();
currentTipList.addAll(tipList);
List<String> suggestionNames = new ArrayList<>();
for (Tip tip : tipList) {
String name = tip.getName();
String district = tip.getDistrict();
String displayText = district != null && !district.isEmpty() ? name + " " + district : name;
suggestionNames.add(displayText);
}
runOnUiThread(() -> {
suggestionAdapter.clear();
suggestionAdapter.addAll(suggestionNames);
suggestionAdapter.notifyDataSetChanged();
if (suggestionNames.size() > 0) {
suggestionList.setVisibility(View.VISIBLE);
} else {
suggestionList.setVisibility(View.GONE);
}
});
} else {
runOnUiThread(() -> {
suggestionList.setVisibility(View.GONE);
});
}
}
private void showKeyboard() {
searchInput.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);
}
}
private void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
}
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 清理回调,避免内存泄漏
callback = null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景颜色:使用浅灰色 -->
<solid android:color="#ffffffff" />
<!-- 圆角8dp左右 -->
<corners android:radius="8dp" />
<!-- 边框:可选,如果需要更清晰的边缘可以加上 -->
<stroke android:width="1dp" android:color="#EAEAEA" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<corners
android:topLeftRadius="24dp"
android:topRightRadius="24dp"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp" />
</shape>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:gravity="center">
<ImageView
android:id="@+id/back_btn"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="10dp"
android:src="@drawable/back" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="选择地点"
android:textColor="#000000"
android:textSize="18sp" />
</LinearLayout>
<EditText
android:id="@+id/search_input"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="12dp"
android:background="@drawable/bg_search_input"
android:hint="输入地址"
android:imeOptions="actionDone"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:singleLine="true"
android:textColor="#333333"
android:textColorHint="#999999"
android:textSize="14sp" />
<ListView
android:id="@+id/suggestion_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="8dp"
android:visibility="gone" />
</LinearLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -21,9 +21,7 @@
.amap-callamap,
.amap-lib-driving-callBtn,
.amap-copyright,
.amap-logo {
display: none !important;
}
.amap-logo{bottom: 60px}
/* 去除高德默认的 label 边框 and 背景 */
.amap-marker-label {
@@ -109,7 +107,7 @@
/* --- 导航结果面板 (底部弹出) --- */
#panel {
position: fixed;
bottom: 75px;
bottom: 95px;
left: 0;
width: 100%;
height: 35%;
@@ -129,7 +127,7 @@
#location-btn {
position: fixed;
right: 10px;
bottom: 75px;
bottom: 105px;
/* 默认位置 */
width: 44px;
height: 44px;
@@ -159,7 +157,7 @@
/* --- 调整比例尺位置 --- */
.amap-scalecontrol {
/* 初始状态:避开底部的定位按钮或留出安全间距 */
bottom: 80px !important;
bottom: 110px !important;
left: 10px !important;
transition: bottom 0.3s ease;
/* 增加平滑动画 */
@@ -195,10 +193,10 @@
<div id="search-box">
<div class="input-row">
<input id="startInput" placeholder="起点: 请输入当前地点" />
<input id="startInput" placeholder="起点: 请输入当前地点" onfocus="this.select()" />
</div>
<div class="input-row">
<input id="endInput" placeholder="终点: 请输入目的地" />
<input id="endInput" placeholder="终点: 请输入目的地" onfocus="this.select()" />
<button onclick="startRouteSearch()">路径规划</button>
</div>
</div>
@@ -244,6 +242,11 @@
}
});
// 点击地图空白处重置状态
map.on('click', function() {
resetSearchState();
});
// 添加基础控件
map.addControl(new AMap.Scale());
map.addControl(new AMap.ToolBar({
@@ -285,6 +288,19 @@
});
}
/**
* 重置搜索状态,隐藏面板和路线
*/
function resetSearchState() {
if (document.body.classList.contains('panel-active')) {
console.log("JS->: 重置地图状态");
document.body.classList.remove('panel-active');
var panel = document.getElementById('panel');
panel.style.display = 'none';
if (driving) driving.clear();
}
}
/**
* 核心功能 1: 接收 Flutter 传来的定位数据
* Flutter 端调用: webViewController.evaluateJavascript("updateMyLocation(...)")
@@ -481,19 +497,25 @@
offset: new AMap.Pixel(-16, -32),
title: station.name,
label: {
content: '<div class="custom-bubble">' + station.name + '</div>',
content: '<div class="custom-bubble">' + station.name +
'</div>',
direction: 'top'
}
});
// 3. 绑定点击事件:选中即为目的地,并开始规划
sMarker.on('click', function() {
document.getElementById('endInput').value = station.address || station.name;
// 更新当前的 destMarker (如果需要)
if (destMarker) destMarker.setMap(null);
sMarker.on('click', function () {
var stationName = station.name || "目的地";
document.getElementById('endInput').value = station.address ||
stationName;
// 更新当前的 destMarker
if (destMarker && destMarker !== sMarker) destMarker.setMap(null);
destMarker = sMarker;
startRouteSearch();
// 直接传入坐标对象,避免关键字搜索失败
var loc = new AMap.LngLat(station.longitude, station.latitude);
startRouteSearch(loc);
});
stationMarkers.push(sMarker);
@@ -554,11 +576,12 @@
}
}
/**
* 路径规划
* 路径规划
* @param {AMap.LngLat} [destLoc] 可选的终点坐标
*/
function startRouteSearch() {
// 获取输入框的文字
function startRouteSearch(destLoc) {
var startKw = document.getElementById('startInput').value;
var endKw = document.getElementById('endInput').value;
@@ -566,28 +589,21 @@
alert("请输入起点");
return;
}
if (!endKw) {
alert("请输入终点");
return;
}
// 清除旧路线
if (driving) driving.clear();
// 收起键盘
document.getElementById('startInput').blur();
document.getElementById('endInput').blur();
// --- 构造路径规划的点 ---
var points = [];
// 1. 处理起点逻辑
// 1. 起点逻辑
if (!startKw || startKw === '我的位置' || startKw.includes('当前位置')) {
if (!currentLng || !currentLat) {
if (window.flutter_inappwebview) {
window.flutter_inappwebview.callHandler('requestLocation');
}
if (window.flutter_inappwebview) window.flutter_inappwebview.callHandler('requestLocation');
alert("正在获取定位,请稍后...");
return;
}
@@ -601,10 +617,17 @@
});
}
// 2. 处理终点逻辑
points.push({
keyword: endKw
});
// 2. 终点逻辑:如果有传入坐标,则直接使用坐标导航,成功率最高
if (destLoc) {
points.push({
keyword: endKw,
location: destLoc // 关键:使用精确坐标
});
} else {
points.push({
keyword: endKw
});
}
// 3. 发起搜索
driving.search(points, function (status, result) {
@@ -613,10 +636,12 @@
var panel = document.getElementById('panel');
panel.style.display = 'block';
document.body.classList.add('panel-active');
} else {
console.log('JS: 规划失败', result);
alert("规划失败,请检查起终点名称");
}
// else {
// console.error('JS: 规划失败', result);
// // 如果坐标规划都失败了,通常是由于起终点距离过近或政策限制(如货车禁行)
// alert("路径规划未成功,请尝试微调起终点");
// }
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 B

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,51 @@
#
# Be sure to run `pod lib lint AMapNavIOSSDK.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'AMapNavIOSSDK'
s.version = '0.1.0'
s.summary = 'A short description of AMapNavIOSSDK.'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/xiaoshuai/AMapNavIOSSDK'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'xiaoshuai' => 'xiaoshuai@net.cn' }
s.source = { :git => 'https://github.com/xiaoshuai/AMapNavIOSSDK.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '12.0'
s.source_files = 'AMapNavIOSSDK/Classes/**/*'
s.resource = 'AMapNavIOSSDK/**/*.bundle'
s.resource_bundles = {
'AMapNavIOSSDKPrivacyInfo' => ['AMapNavIOSSDK/**/PrivacyInfo.xcprivacy']
}
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
s.dependency 'Masonry'
s.dependency 'MJExtension'
s.dependency 'AMapNavi-NO-IDFA'
s.dependency 'AMapLocation-NO-IDFA'
s.dependency 'AMapSearch-NO-IDFA'
s.dependency 'MBProgressHUD'
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>0A2A.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>85F4.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
</dict>
</plist>

View File

@@ -0,0 +1,30 @@
//
// AAddHPopView.h
// AMapNavIOSSDK
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
* 气泡弹框视图
*
* 用法:
* AAddHPopView *pop = [[AAddHPopView alloc] init];
* [pop showInView:self.view sourceView:addHbtn];
*
* - 点击气泡外部自动消失
* - 箭头在右下角45度指向 sourceView 中心
*/
@interface AAddHPopView : UIView
/// 展示气泡sourceView 为箭头指向的锚定按钮(坐标系属于 view
- (void)showInView:(UIView *)view sourceView:(UIView *)sourceView;
/// 手动消失(点击空白处会自动调用)
- (void)dismiss;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,359 @@
//
// AAddHPopView.m
// AMapNavIOSSDK
//
#import "AAddHPopView.h"
#import <Masonry/Masonry.h>
#import "UIColor+ANavMap.h"
//
static const CGFloat kBubbleWidth = 200.f; //
static const CGFloat kHeaderHeight = 50.f; // 绿
static const CGFloat kOptionHeight = 60.f; //
static const CGFloat kArrowWidth = 50.f; // aw = ah*2 使45
static const CGFloat kArrowHeight = 15.f; //
static const CGFloat kBubbleRadius = 15.f; //
static const CGFloat kBubbleGap = 5.f; // sourceView
static const CGFloat kScreenPadding = 15.f; //
// +
@interface _ABubbleContainerView : UIView
/// X view showInView
@property (nonatomic, assign) CGFloat arrowCenterX;
@end
@implementation _ABubbleContainerView
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor];
_arrowCenterX = kBubbleWidth / 2.f;
}
return self;
}
- (void)setArrowCenterX:(CGFloat)arrowCenterX {
_arrowCenterX = arrowCenterX;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
CGFloat bw = kBubbleWidth; // 160
CGFloat bh = kHeaderHeight + kOptionHeight * 2.f; // 150
CGFloat r = kBubbleRadius; // 10
CGFloat aw = kArrowWidth; // 16
CGFloat ah = kArrowHeight; // 10
// 45
// : (arrowX-aw/2, bh) (arrowX, bh)
// : (arrowX + ah, bh + ah) 45
CGFloat arrowX = bw - 15.f; // X
CGFloat arrowTipX = arrowX + ah; // Xah = 45
CGFloat arrowTipY = bh + ah; // Yah = 45
UIBezierPath *path = [UIBezierPath bezierPath];
//
[path moveToPoint:CGPointMake(r, 0)];
[path addLineToPoint:CGPointMake(bw - r, 0)];
[path addArcWithCenter:CGPointMake(bw - r, r) radius:r
startAngle:-M_PI_2 endAngle:0 clockwise:YES];
//
[path addLineToPoint:CGPointMake(bw, bh - r)];
[path addArcWithCenter:CGPointMake(bw - r, bh - r) radius:r
startAngle:0 endAngle:M_PI_2 clockwise:YES];
//
[path addLineToPoint:CGPointMake(arrowX, bh)];
// 45
[path addLineToPoint:CGPointMake(arrowTipX, arrowTipY)];
//
[path addLineToPoint:CGPointMake(arrowX - ah, bh)];
//
[path addArcWithCenter:CGPointMake(r, bh - r) radius:r
startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
//
[path addLineToPoint:CGPointMake(0, r)];
[path addArcWithCenter:CGPointMake(r, r) radius:r
startAngle:M_PI endAngle:-M_PI_2 clockwise:YES];
[path closePath];
// +
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 3), 10.f,
[[UIColor blackColor] colorWithAlphaComponent:0.15f].CGColor);
[[UIColor whiteColor] setFill];
[path fill];
CGContextRestoreGState(ctx);
[[UIColor whiteColor] setFill];
[path fill];
//
_arrowTipX = arrowTipX;
_arrowTipY = arrowTipY;
}
// 访
static CGFloat _arrowTipX = 0;
static CGFloat _arrowTipY = 0;
@end
// AAddHPopView
@interface AAddHPopView ()
@property (nonatomic, strong) _ABubbleContainerView *bubbleContainer;
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIView *separatorLine1;
@property (nonatomic, strong) UILabel *option1Label;
@property (nonatomic, strong) UIView *separatorLine2;
@property (nonatomic, strong) UILabel *option2Label;
@end
@implementation AAddHPopView
#pragma mark - Init
- (instancetype)init {
return [self initWithFrame:CGRectZero];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self p_setupUI];
}
return self;
}
#pragma mark - Setup UI
- (void)p_setupUI {
self.backgroundColor = [UIColor clearColor];
// self
UITapGestureRecognizer *bgTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(dismiss)];
bgTap.cancelsTouchesInView = NO;
[self addGestureRecognizer:bgTap];
//
[self addSubview:self.bubbleContainer];
// header绿
[self.bubbleContainer addSubview:self.headerView];
[self.headerView addSubview:self.titleLabel];
// 线 1
[self.bubbleContainer addSubview:self.separatorLine1];
// 1
[self.bubbleContainer addSubview:self.option1Label];
// 线 2
[self.bubbleContainer addSubview:self.separatorLine2];
// 2
[self.bubbleContainer addSubview:self.option2Label];
// bgTap
UITapGestureRecognizer *bubbleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(p_bubbleTapped)];
[self.bubbleContainer addGestureRecognizer:bubbleTap];
// Masonry bubbleContainer
// kBubbleWidth160
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(self.bubbleContainer);
make.width.mas_equalTo(kBubbleWidth);
make.height.mas_equalTo(kHeaderHeight);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.headerView);
}];
[self.separatorLine1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.headerView.mas_bottom);
make.left.mas_equalTo(@0);
make.width.mas_equalTo(kBubbleWidth);
make.height.mas_equalTo(0.5);
}];
[self.option1Label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.separatorLine1.mas_bottom);
make.left.mas_equalTo(@0);
make.width.mas_equalTo(kBubbleWidth);
make.height.mas_equalTo(kOptionHeight);
}];
[self.separatorLine2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.option1Label.mas_bottom);
make.left.mas_equalTo(@0);
make.width.mas_equalTo(kBubbleWidth);
make.height.mas_equalTo(0.5);
}];
[self.option2Label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.separatorLine2.mas_bottom);
make.left.mas_equalTo(@0);
make.width.mas_equalTo(kBubbleWidth);
make.height.mas_equalTo(kOptionHeight);
}];
}
#pragma mark - Show / Dismiss
- (void)showInView:(UIView *)view sourceView:(UIView *)sourceView {
self.frame = view.bounds;
[view addSubview:self];
// sourceView view frame
CGRect srcFrame = [sourceView convertRect:sourceView.bounds toView:view];
//
CGFloat containerW = kBubbleWidth; // 160
CGFloat containerH = kHeaderHeight + kOptionHeight * 2.f; // 150
CGFloat ah = kArrowHeight; // 16
// bubbleContainer
// drawRect arrowX = bw - 35, arrowTipX = arrowX + ah
CGFloat arrowX_inContainer = containerW - 25.f; // X
CGFloat arrowTipX_inContainer = arrowX_inContainer + ah; // X45
CGFloat arrowTipY_inContainer = containerH + ah; // Y
//
CGFloat srcCenterX = CGRectGetMidX(srcFrame);
CGFloat srcCenterY = CGRectGetMidY(srcFrame);
//
CGFloat containerX = srcCenterX - arrowTipX_inContainer;
containerX = MAX(kScreenPadding, MIN(containerX, view.bounds.size.width - containerW - kScreenPadding));
//
CGFloat containerY = srcCenterY - arrowTipY_inContainer;
containerY = MAX(kScreenPadding, containerY);
self.bubbleContainer.bounds = CGRectMake(0, 0, containerW, containerH + ah);
//
self.bubbleContainer.layer.anchorPoint = CGPointMake(arrowTipX_inContainer / containerW,
arrowTipY_inContainer / (containerH + ah));
self.bubbleContainer.center = CGPointMake(srcCenterX - 25, srcCenterY - 20);
//
self.alpha = 0.f;
self.bubbleContainer.transform = CGAffineTransformMakeScale(0.85f, 0.85f);
[UIView animateWithDuration:0.25f
delay:0.f
usingSpringWithDamping:0.72f
initialSpringVelocity:0.3f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.alpha = 1.f;
self.bubbleContainer.transform = CGAffineTransformIdentity;
} completion:nil];
}
- (void)dismiss {
[UIView animateWithDuration:0.18f
delay:0.f
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.alpha = 0.f;
self.bubbleContainer.transform = CGAffineTransformMakeScale(0.85f, 0.85f);
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
#pragma mark - Private Actions
- (void)p_bubbleTapped {
//
}
#pragma mark - Lazy Load
- (_ABubbleContainerView *)bubbleContainer {
if (!_bubbleContainer) {
_bubbleContainer = [[_ABubbleContainerView alloc] init];
}
return _bubbleContainer;
}
- (UIView *)headerView {
if (!_headerView) {
_headerView = [[UIView alloc] init];
_headerView.backgroundColor = [UIColor hp_colorWithRGBHex:0x1BA855];
if (@available(iOS 11.0, *)) {
_headerView.layer.cornerRadius = kBubbleRadius;
_headerView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
_headerView.clipsToBounds = YES;
}
}
return _headerView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.text = @"加氢规划模式";
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.font = [UIFont boldSystemFontOfSize:15.f];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
- (UIView *)separatorLine1 {
if (!_separatorLine1) {
_separatorLine1 = [[UIView alloc] init];
_separatorLine1.backgroundColor = [UIColor colorWithWhite:0.88f alpha:1.f];
}
return _separatorLine1;
}
- (UILabel *)option1Label {
if (!_option1Label) {
_option1Label = [[UILabel alloc] init];
_option1Label.text = @"送货规划模式";
_option1Label.textColor = [UIColor hp_colorWithRGBHex:0xC9CDD4];
_option1Label.font = [UIFont boldSystemFontOfSize:14.f];
_option1Label.textAlignment = NSTextAlignmentCenter;
}
return _option1Label;
}
- (UIView *)separatorLine2 {
if (!_separatorLine2) {
_separatorLine2 = [[UIView alloc] init];
_separatorLine2.backgroundColor = [UIColor colorWithWhite:0.88f alpha:1.f];
}
return _separatorLine2;
}
- (UILabel *)option2Label {
if (!_option2Label) {
_option2Label = [[UILabel alloc] init];
_option2Label.text = @"成本计算模式";
_option2Label.textColor = [UIColor hp_colorWithRGBHex:0xC9CDD4];
_option2Label.font = [UIFont boldSystemFontOfSize:14.f];
_option2Label.textAlignment = NSTextAlignmentCenter;
}
return _option2Label;
}
@end

View File

@@ -0,0 +1,24 @@
//
// ABaseViewController.h
// ANavDemo
//
// Created by admin on 2026/2/5.
//
#import <UIKit/UIKit.h>
#import <Masonry/Masonry.h>
#import "AMapNavCommonUtil.h"
#define kRoutePlanBarHeight (self.navigationController.navigationBar.frame.size.height + UIApplication.sharedApplication.statusBarFrame.size.height + 0)
#define kRoutePlanStatusBarHeight (UIApplication.sharedApplication.statusBarFrame.size.height + 0)
NS_ASSUME_NONNULL_BEGIN
@interface ABaseViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
//
// ABaseViewController.m
// ANavDemo
//
// Created by admin on 2026/2/5.
//
#import "ABaseViewController.h"
@interface ABaseViewController ()
@end
@implementation ABaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end

View File

@@ -0,0 +1,17 @@
//
// ACustomAnnotationView.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/17.
//
#import <AMapNaviKit/AMapNaviKit.h>
#import "AMapNavCommonUtil.h"
NS_ASSUME_NONNULL_BEGIN
@interface ACustomAnnotationView : MAAnnotationView
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,177 @@
//
// ACustomAnnotationView.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/17.
//
#import "ACustomAnnotationView.h"
//
static const CGFloat kIconSize = 30; //
static const CGFloat kIconTextGap = 6.0; //
static const CGFloat kMaxTextWidth = 100.0; //
static const CGFloat kMaxTextLines = 2; //
static const CGFloat kVPad = 4.0; // centerOffset
@interface ACustomAnnotationView ()
@property (nonatomic, strong) UIImageView *iconView;
@property (nonatomic, strong) UILabel *titleLabel;
/// &
@property (nonatomic, assign) BOOL isAnnotationSelected;
@end
@implementation ACustomAnnotationView
#pragma mark - Init / Reuse
- (instancetype)initWithAnnotation:(id<MAAnnotation>)annotation
reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
self.backgroundColor = [UIColor clearColor];
// MAAnnotationView image
self.image = nil;
[self _buildSubviews];
[self _updateFromAnnotation];
[self setNeedsLayout];
}
return self;
}
- (void)setAnnotation:(id<MAAnnotation>)annotation {
[super setAnnotation:annotation];
[self _updateFromAnnotation];
[self setNeedsLayout];
}
#pragma mark - Build
- (void)_buildSubviews {
//
if (!self.iconView) {
UIImageView *iv = [[UIImageView alloc] init];
iv.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:iv];
self.iconView = iv;
}
//
if (!self.titleLabel) {
UILabel *lbl = [[UILabel alloc] init];
lbl.numberOfLines = kMaxTextLines;
lbl.lineBreakMode = NSLineBreakByTruncatingTail;
lbl.textAlignment = NSTextAlignmentLeft;
[self addSubview:lbl];
self.titleLabel = lbl;
}
//
[self _applyStyle:NO];
}
/// &
- (void)_applyStyle:(BOOL)selected {
if (selected) {
self.iconView.image = [AMapNavCommonUtil imageWithName3x:@"station_select_icon"];
self.titleLabel.font = [UIFont boldSystemFontOfSize:14];
self.titleLabel.textColor = [UIColor colorWithWhite:0.12 alpha:1.0];
} else {
self.iconView.image = [AMapNavCommonUtil imageWithName3x:@"station_normal_icon"];
self.titleLabel.font = [UIFont systemFontOfSize:13];
self.titleLabel.textColor = [UIColor colorWithWhite:0.25 alpha:1.0];
}
}
#pragma mark - Data
- (void)_updateFromAnnotation {
NSString *text = nil;
id<MAAnnotation> ann = self.annotation;
if ([ann respondsToSelector:@selector(title)]) {
text = ann.title;
}
if (text.length == 0) {
text = @"加氢站";
}
self.titleLabel.text = text;
}
#pragma mark - SelectionMAAnnotationView
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
self.isAnnotationSelected = selected;
[self _applyStyle:selected];
[self setNeedsLayout];
}
#pragma mark - Layout
- (void)layoutSubviews {
[super layoutSubviews];
// 100pt 2
CGSize textConstraint = CGSizeMake(kMaxTextWidth,
CGFLOAT_MAX);
CGSize textFit = [self.titleLabel sizeThatFits:textConstraint];
// 2
CGFloat lineH = self.titleLabel.font.lineHeight;
CGFloat maxTextH = lineH * kMaxTextLines
+ self.titleLabel.font.leading * (kMaxTextLines - 1);
CGFloat textW = MIN(textFit.width, kMaxTextWidth);
CGFloat textH = MIN(textFit.height, maxTextH);
CGFloat totalW = kIconSize + kIconTextGap + textW;
CGFloat totalH = MAX(kIconSize, textH) + kVPad * 2;
// bounds
self.bounds = CGRectMake(0, 0, totalW, totalH);
//
CGFloat iconY = (totalH - kIconSize) * 0.5;
self.iconView.frame = CGRectMake(0, iconY, kIconSize, kIconSize);
//
CGFloat textY = (totalH - textH) * 0.5;
self.titleLabel.frame = CGRectMake(kIconSize + 1, textY, textW, textH);
//
// MAAnnotationView centerOffset (0,0)=
// totalH/2
self.centerOffset = CGPointMake(totalW / 2.0 - kIconSize / 2.0,
-(totalH / 2.0));
}
#pragma mark - Hit Test
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hit = [super hitTest:point withEvent:event];
if (hit == self || [hit isDescendantOfView:self]) {
return self;
}
return hit;
}
#pragma mark - Touch
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
id<MAAnnotation> annotation = self.annotation;
if (!annotation) return;
UIView *sv = self.superview;
while (sv && ![sv isKindOfClass:[MAMapView class]]) {
sv = sv.superview;
}
MAMapView *mapView = (MAMapView *)sv;
if (!mapView) return;
// [mapView deselectAnnotation:annotation animated:NO];
}
@end

View File

@@ -0,0 +1,16 @@
//
// ACustomNaviDriveController.h
// AMapNavIOSSDK
//
// Created by admin on 2026/4/11.
//
#import "ABaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface ACustomNaviDriveController : ABaseViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,22 @@
//
// ACustomNaviDriveController.m
// AMapNavIOSSDK
//
// Created by admin on 2026/4/11.
//
#import "ACustomNaviDriveController.h"
@interface ACustomNaviDriveController ()
@end
@implementation ACustomNaviDriveController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
@end

View File

@@ -0,0 +1,51 @@
//
// AMapNavSDKHeader.h
// Pods
//
// Created by admin on 2026/2/10.
//
#ifndef AMapNavSDKHeader_h
#define AMapNavSDKHeader_h
//#define kAMapSDKDebugFlag
// iPhone X
#define AMP_iPhoneX (UIApplication.sharedApplication.keyWindow.safeAreaInsets.bottom > 0 ? YES : NO)
// Status bar height.
#define AMP_StatusBarHeight (AMP_iPhoneX ? 44.f : 20.f)
// Navigation bar height.
#define AMP_NavigationBarHeight 44.f
// Tabbar height.
#define AMP_TabbarHeight (AMP_iPhoneX ? (49.f+34.f + 5) : 49.f)
// Tabbar safe bottom margin.
#define AMP_TabbarSafeBottomMargin (AMP_iPhoneX ? 34.f : 0.f)
#pragma mark - url
///获取站点列表
#define kGetStationListUrl @"https://beta-esg.api.lnh2e.com/appointment/station/getNearbyHydrogenStationsByLocation"
///单个站点详情
#define kGetStationDetailtUrl @"https://beta-esg.api.lnh2e.com/appointment/station/getStationInfoByArea"
///获取途经点
/**
请求方式post 暂时调不通
{
   "longitude":"121.254139",
    "latitude":"31.214628",
    "plateNumber":"浙F32111F",
"hydrogenSiteId":""//加氢站DI }
*/
#define kGetRoutePointtUrl @"https://beta-esg.api.lnh2e.com/appointment/truck/truckRouteAlgorithm"
#import "ANavPointModel.h"
#endif /* AMapNavSDKHeader_h */

View File

@@ -0,0 +1,29 @@
//
// AMapNavSDKManager.h
// Pods
//
// Created by admin on 2026/2/10.
//
#import <Foundation/Foundation.h>
#import "ARoutePlaneController.h"
NS_ASSUME_NONNULL_BEGIN
@interface AMapNavSDKManager : NSObject
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic , strong) NSString * localCity;
@property (nonatomic, copy) NSString * locationAddressDetail;
@property (nonatomic , strong , readonly) UIViewController * targetVC;
+ (instancetype)sharedManager;
- (void)configWithKey:(NSString*)key;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,72 @@
//
// AMapNavSDKManager.m
// Pods
//
// Created by admin on 2026/2/10.
//
#import "AMapNavSDKManager.h"
#import "AMapPrivacyUtility.h"
#import <AMapFoundationKit/AMapFoundationKit.h>
@interface AMapNavSDKManager ()
@property (nonatomic , strong , readwrite) UIViewController * targetVC;
@end
@implementation AMapNavSDKManager
+ (instancetype)sharedManager {
static AMapNavSDKManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[AMapNavSDKManager alloc] init];
});
return manager;
}
- (instancetype)init {
self = [super init];
if (self) {
_targetVC = [ARoutePlaneController new];
}
return self;
}
- (void)configWithKey:(NSString*)key {
if (1) {
/*
*
*/
// [AMapPrivacyUtility handlePrivacyAgreeStatusIn:_targetVC];
//
// SDK
[self configureAPIKey:key];
}
}
- (UIViewController *)targetVC {
return _targetVC;
}
#pragma mark - private
- (void)configureAPIKey:(NSString*)key {
if ([key length] == 0)
{
NSString *reason = [NSString stringWithFormat:@"apiKey为空请检查key是否正确设置。"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:reason delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
[AMapServices sharedServices].enableHTTPS = YES;
[AMapServices sharedServices].apiKey = (NSString *)key;
}
@end

View File

@@ -0,0 +1,28 @@
//
// ARoutePlaneController.h
// ANavDemo
//
// Created by admin on 2026/2/5.
//
#import <UIKit/UIKit.h>
#import "ABaseViewController.h"
#import "SelectableOverlay.h"
#import "NaviPointAnnotation.h"
#import <AMapNaviKit/AMapNaviKit.h>
#import "ACustomAnnotationView.h"
#import "AMapNavSDKHeader.h"
#import "ACustomPointAnnotation.h"
#import "ATripCalcResponse.h"
#import "AMapNavCommonUtil.h"
NS_ASSUME_NONNULL_BEGIN
@interface ARoutePlaneController : ABaseViewController<AMapNaviDriveDataRepresentable>
@property (nonatomic , strong) AMapNaviDriveView * driveView;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
//
// ASearchAddressController.h
// ANavDemo
//
// Created by admin on 2026/2/6.
//
#import "ABaseViewController.h"
#import <AMapSearchKit/AMapSearchKit.h>
#import <AMapLocationKit/AMapLocationKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASearchAddressController : ABaseViewController
@property (nonatomic , copy) void(^selectAddressBlk)(AMapPOI * poi);
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,289 @@
//
// ASearchAddressController.m
// ANavDemo
//
// Created by admin on 2026/2/6.
//
#import "ASearchAddressController.h"
#import "AMapNavSDKManager.h"
#import <AMapFoundationKit/AMapFoundationKit.h>
#import "AMapNavCommonUtil.h"
@interface ASearchAddressController () <UITextFieldDelegate, AMapSearchDelegate, UITableViewDelegate, UITableViewDataSource>
@property(nonatomic, strong) UITableView *tableView;
@property(nonatomic, strong) UIBarButtonItem *rightItem;
@property(nonatomic, strong) UIButton *backBtn;
@property(nonatomic, strong) NSMutableArray *dataArr;
@property(nonatomic, strong) UITextField *inputAddressTf;
@property(nonatomic, strong) AMapSearchAPI *search;
@property(nonatomic, assign) NSInteger currPage;
@end
@implementation ASearchAddressController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"选择地点";
self.currPage = 1;
_dataArr = [NSMutableArray array];
[self initSubview];
self.search = [[AMapSearchAPI alloc] init];
self.search.delegate = self;
#ifdef kAMapSDKDebugFlag
self.inputAddressTf.text = @"人民广场";
#endif
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
dispatch_async(dispatch_get_main_queue(), ^{
[self.inputAddressTf becomeFirstResponder];
});
}
- (void)initSubview {
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.backBtn];
UITextField *inputAddressTf = [[UITextField alloc] init];
inputAddressTf.borderStyle = UITextBorderStyleRoundedRect;
inputAddressTf.placeholder = @"输入地址";
inputAddressTf.returnKeyType = UIReturnKeySearch;
inputAddressTf.tag = 100;
inputAddressTf.delegate = self;
[self.view addSubview:inputAddressTf];
[inputAddressTf mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view).offset(10);
make.top.mas_equalTo(self.view).offset(kRoutePlanBarHeight + 10);
make.height.mas_equalTo(@30);
}];
self.inputAddressTf = inputAddressTf;
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setTitle:@"当前位置" forState:UIControlStateNormal];
btn.backgroundColor = [UIColor whiteColor];
btn.layer.borderColor = [UIColor blueColor].CGColor;
btn.layer.borderWidth = 1;
btn.layer.cornerRadius = 5;
btn.titleLabel.font = [UIFont systemFontOfSize:12];
btn.hidden = YES;
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(searchBtnAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(inputAddressTf.mas_right).offset(12);
make.top.mas_equalTo(inputAddressTf);
make.right.mas_equalTo(self.view).offset(-10);
make.height.mas_equalTo(@30);
make.width.mas_equalTo(70);
}];
[self.view addSubview:self.tableView];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(inputAddressTf.mas_bottom).offset(5);
make.left.equalTo(self.view);
make.bottom.equalTo(self.view);
make.centerX.equalTo(self.view);
}];
[self.tableView reloadData];
}
#pragma mark -
- (nonnull UITableViewCell
*)tableView:(
nonnull UITableView
*)
tableView cellForRowAtIndexPath
:(
nonnull NSIndexPath
*)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"cell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
AMapPOI *m = self.dataArr[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"%@", m.name];
return cell;
}
- (NSInteger)tableView:(nonnull UITableView
*)
tableView numberOfRowsInSection
:(NSInteger)section {
return self.dataArr.count;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
AMapPOI *m = self.dataArr[indexPath.row];
if (self.selectAddressBlk) {
self.selectAddressBlk(m);
}
[self backBtnAction];
// [self.navigationController popViewControllerAnimated:YES];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == self.dataArr.count - 1 && (self.dataArr.count % 20 == 0)) {
self.currPage = self.currPage + 1;
[self requestAddressWithAddress:self.inputAddressTf.text atPage:self.currPage];
}
}
#pragma mark -
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
}
return _tableView;
}
- (UIButton *)backBtn {
if (!_backBtn) {
_backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_backBtn.frame = CGRectMake(0, 0, 40, 30);
_backBtn.imageEdgeInsets = UIEdgeInsetsMake(2, -5, 2, 5);
// _backBtn.backgroundColor = UIColor.redColor;
[_backBtn setImage:[AMapNavCommonUtil imageWithName:@"icon_fanhui"] forState:UIControlStateNormal];
[_backBtn addTarget:self action:@selector(backBtnAction) forControlEvents:UIControlEventTouchUpInside];
}
return _backBtn;
}
- (void)backBtnAction {
[self dismissViewControllerAnimated:YES completion:^{
}];
}
#pragma mark - Action
- (void)searchBtnAction {
AMapNavSDKManager *sdk = [AMapNavSDKManager sharedManager];
self.inputAddressTf.text = sdk.locationAddressDetail;
[self startSearchWithAddress:self.inputAddressTf.text];
}
- (void)startSearchWithAddress:(NSString *)addr {
if (!addr) {
return;
}
AMapPOIKeywordsSearchRequest *request = [[AMapPOIKeywordsSearchRequest alloc] init];
request.keywords = addr;
AMapNavSDKManager *sdk = [AMapNavSDKManager sharedManager];
request.city = sdk.localCity;
// request.types = @"高等院校";
// request.requireExtension = YES;
request.offset = 20;
request.page = 1;
/* SDK 3.2.0 POI*/
// request.cityLimit = YES;
// request.requireSubPOIs = YES;
[self.search AMapPOIKeywordsSearch:request];
}
- (void)requestAddressWithAddress:(NSString *)addr atPage:(NSInteger)page {
AMapPOIKeywordsSearchRequest *request = [[AMapPOIKeywordsSearchRequest alloc] init];
request.keywords = addr;
AMapNavSDKManager *sdk = [AMapNavSDKManager sharedManager];
request.city = sdk.localCity;
// request.types = @"高等院校";
// request.requireExtension = YES;
request.offset = 20;
request.page = page;
/* SDK 3.2.0 POI*/
request.cityLimit = YES;
// request.requireSubPOIs = YES;
[self.search AMapPOIKeywordsSearch:request];
}
#pragma mark -
/* POI . */
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response {
NSArray *pois = response.pois;
if (pois.count == 0) {
return;
}
//responsePOI Demo
[self.dataArr addObjectsFromArray:pois];
[self.tableView reloadData];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
[self.dataArr removeAllObjects];
self.currPage = 1;
[self startSearchWithAddress:textField.text];
return YES;
}
- (void)touchesBegan:(NSSet
<UITouch *> *)
touches withEvent
:(UIEvent *)event {
[self.inputAddressTf resignFirstResponder];
}
@end

View File

@@ -0,0 +1,71 @@
//
// AStationDetailPopupController.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/22.
//
#import <UIKit/UIKit.h>
#import "ANavPointModel.h"
#import "AMapNavSDKHeader.h"
NS_ASSUME_NONNULL_BEGIN
@class AStationDetailPopupController;
@protocol AStationDetailPopupDelegate <NSObject>
@optional
/// 点击"开始导航"
- (void)stationDetailPopupDidTapStartNavi:(AStationDetailPopupController *)popup;
/// 点击关闭
- (void)stationDetailPopupDidTapClose:(AStationDetailPopupController *)popup;
@end
@interface AStationDetailPopupController : UIViewController
@property (nonatomic, strong, nullable) ANavPointModel *pointModel;
/// 预计加氢费用(元),可由外部传入;若 nil 则隐藏
@property (nonatomic, copy, nullable) NSString *estimatedCost;
/// 预计时间,如 @"15分钟";若 nil 则隐藏
@property (nonatomic, copy, nullable) NSString *estimatedTime;
/// 行驶里程,如 @"23.5公里";若 nil 则隐藏
@property (nonatomic, copy, nullable) NSString *driveDistance;
/// 过路费,如 @"30元";若 nil 则隐藏
@property (nonatomic, copy, nullable) NSString *tollFee;
/// 营业时间,如 @"00:00-24:00";有值显示,无值显示 -
@property (nonatomic, copy, nullable) NSString *businessHours;
/// 站点联系人,如 @"陈凯";有值显示,无值显示 -
@property (nonatomic, copy, nullable) NSString *contactName;
/// 联系方式,如 @"18019187371";有值显示,无值显示 -
@property (nonatomic, copy, nullable) NSString *contactPhone;
/// 加氢价格,如 @"32元/L";有值显示,无值显示 -
@property (nonatomic, copy, nullable) NSString *hydrogenPrice;
@property (nonatomic, weak, nullable) id<AStationDetailPopupDelegate> delegate;
/// 以半透明蒙层方式弹出在目标控制器上
- (void)presentInViewController:(UIViewController *)parentVC;
/// 关闭弹框
- (void)dismiss;
/// 关闭弹框,动画结束后执行 completion用于关闭后再 present 其他页面)
- (void)dismissWithCompletion:(nullable void(^)(void))completion;
-(void)resetUI;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,654 @@
//
// AStationDetailPopupController.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/22.
//
#import "AStationDetailPopupController.h"
#import "ABaseViewController.h"
#import "AMapNavCommonUtil.h"
#import <Masonry/Masonry.h>
// 绿
static inline UIColor *AStationThemeGreen(void) {
return [UIColor colorWithRed:0x1A/255.0 green:0x7C/255.0 blue:0x43/255.0 alpha:1.0];
}
@interface AStationDetailPopupController ()
///
@property (nonatomic, strong) UIControl *maskControl;
///
@property (nonatomic, strong) UIView *cardView;
///
@property (nonatomic, strong) UILabel *stationNameLabel;
///
@property (nonatomic, strong) UIImageView *costIconView;
@property (nonatomic, strong) UILabel *costLabel;
///
@property (nonatomic, strong) UILabel *addressLabel;
/// 线
@property (nonatomic, strong) UIView *separator;
///
@property (nonatomic, strong) UIImageView *timeIconView;
@property (nonatomic, strong) UILabel *timeLabel;
///
@property (nonatomic, strong) UIImageView *distanceIconView;
@property (nonatomic, strong) UILabel *distanceLabel;
///
@property (nonatomic, strong) UIImageView *tollIconView;
@property (nonatomic, strong) UILabel *tollLabel;
///
@property (nonatomic, strong) UIButton *closeButton;
///
@property (nonatomic, strong) UIButton *startNaviButton;
/// bottom constraint
@property (nonatomic, strong) MASConstraint *cardBottomConstraint;
//
@property (nonatomic, strong) UILabel *businessHoursLabel;
//
@property (nonatomic, strong) UIImageView *contactPersonIconView;
@property (nonatomic, strong) UILabel *contactPersonLabel;
//
@property (nonatomic, strong) UIImageView *priceIconView;
@property (nonatomic, strong) UILabel *priceLabel;
//
@property (nonatomic, strong) UIImageView *phoneIconView;
@property (nonatomic, strong) UILabel *phoneLabel;
@end
@implementation AStationDetailPopupController
#pragma mark - Life Cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
[self _buildUI];
[self _setupMasonryConstraints];
[self _updateUI];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self _playShowAnimation];
}
#pragma mark - Public
- (void)presentInViewController:(UIViewController *)parentVC {
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[parentVC presentViewController:self animated:NO completion:nil];
}
- (void)dismiss {
[self dismissWithCompletion:nil];
}
- (void)dismissWithCompletion:(void(^)(void))completion {
[self _playDismissAnimationWithCompletion:^{
[self dismissViewControllerAnimated:NO completion:completion];
}];
}
#pragma mark - Setter Override
- (void)setPointModel:(ANavPointModel *)pointModel {
_pointModel = pointModel;
if (self.isViewLoaded) [self _updateUI];
}
- (void)setEstimatedCost:(NSString *)estimatedCost {
_estimatedCost = [estimatedCost copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setEstimatedTime:(NSString *)estimatedTime {
_estimatedTime = [estimatedTime copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setDriveDistance:(NSString *)driveDistance {
_driveDistance = [driveDistance copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setTollFee:(NSString *)tollFee {
_tollFee = [tollFee copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setBusinessHours:(NSString *)businessHours {
_businessHours = [businessHours copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setContactName:(NSString *)contactName {
_contactName = [contactName copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setContactPhone:(NSString *)contactPhone {
_contactPhone = [contactPhone copy];
if (self.isViewLoaded) [self _updateUI];
}
- (void)setHydrogenPrice:(NSString *)hydrogenPrice {
_hydrogenPrice = [hydrogenPrice copy];
if (self.isViewLoaded) [self _updateUI];
}
#pragma mark - Build UI
/**
[]
-- 20pt
[icon] --
[icon] -- [icon] --
(30pt)
*/
- (void)_buildUI {
//
UIControl *mask = [[UIControl alloc] init];
mask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.35];
[mask addTarget:self action:@selector(_onMaskTapped) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:mask];
self.maskControl = mask;
//
UIView *card = [[UIView alloc] init];
card.backgroundColor = [UIColor whiteColor];
card.layer.cornerRadius = 16;
card.layer.masksToBounds = NO;
card.layer.shadowColor = [UIColor blackColor].CGColor;
card.layer.shadowOpacity = 0.15;
card.layer.shadowRadius = 12;
card.layer.shadowOffset = CGSizeMake(0, -4);
[self.view addSubview:card];
self.cardView = card;
//
UIButton *closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
// [closeBtn setTitle:@"✕" forState:UIControlStateNormal];
[closeBtn setImage: [AMapNavCommonUtil imageWithName:@"icon_close"] forState:UIControlStateNormal];
[closeBtn setTitleColor:[UIColor colorWithWhite:0.5 alpha:1] forState:UIControlStateNormal];
closeBtn.titleLabel.font = [UIFont systemFontOfSize:16];
[closeBtn addTarget:self action:@selector(_onCloseTapped) forControlEvents:UIControlEventTouchUpInside];
[card addSubview:closeBtn];
self.closeButton = closeBtn;
UIColor * headTextColor = [UIColor hp_colorWithRGBHex:0x1D2129];
UIFont * headTextFont = [UIFont hp_pingFangMedium:14];
//
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.font = [UIFont hp_pingFangMedium:18];
nameLabel.textColor = headTextColor;
nameLabel.numberOfLines = 2;
// nameLabel.adjustsFontSizeToFitWidth = YES;
nameLabel.minimumScaleFactor = 0.8;
[card addSubview:nameLabel];
self.stationNameLabel = nameLabel;
// 20pt
UILabel *costLabel = [[UILabel alloc] init];
costLabel.font = headTextFont;
costLabel.textColor = headTextColor;
costLabel.numberOfLines = 1;
costLabel.textAlignment = NSTextAlignmentLeft;
// [costLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
// [costLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[card addSubview:costLabel];
self.costLabel = costLabel;
// 4pt
UILabel *bizHoursLabel = [[UILabel alloc] init];
bizHoursLabel.font = [UIFont hp_pingFangRegular:14];
bizHoursLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
bizHoursLabel.numberOfLines = 1;
[card addSubview:bizHoursLabel];
self.businessHoursLabel = bizHoursLabel;
//
UILabel *addrLabel = [[UILabel alloc] init];
addrLabel.font = [UIFont hp_pingFangRegular:14];
addrLabel.textColor = [UIColor hp_colorWithRGBHex:0x86909C];
addrLabel.numberOfLines = 2;
[card addSubview:addrLabel];
self.addressLabel = addrLabel;
UIImageView *costIcon = [[UIImageView alloc] init];
costIcon.contentMode = UIViewContentModeScaleAspectFit;
costIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_fuel"];
[card addSubview:costIcon];
self.costIconView = costIcon;
// 线
UIView *sep = [[UIView alloc] init];
sep.backgroundColor = [UIColor colorWithWhite:0.88 alpha:1];
[card addSubview:sep];
self.separator = sep;
//
UIImageView *timeIcon = [[UIImageView alloc] init];
timeIcon.contentMode = UIViewContentModeScaleAspectFit;
timeIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_time"];
[card addSubview:timeIcon];
self.timeIconView = timeIcon;
//
UILabel *timeLabel = [[UILabel alloc] init];
timeLabel.font = headTextFont;
timeLabel.textColor = headTextColor;
[card addSubview:timeLabel];
self.timeLabel = timeLabel;
//
UIImageView *distIcon = [[UIImageView alloc] init];
distIcon.contentMode = UIViewContentModeScaleAspectFit;
distIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_mileage"];
[card addSubview:distIcon];
self.distanceIconView = distIcon;
//
UILabel *distLabel = [[UILabel alloc] init];
distLabel.font = headTextFont;
distLabel.textColor = headTextColor;
[card addSubview:distLabel];
self.distanceLabel = distLabel;
//
UIImageView *tollIcon = [[UIImageView alloc] init];
tollIcon.contentMode = UIViewContentModeScaleAspectFit;
tollIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_toll"];
[card addSubview:tollIcon];
self.tollIconView = tollIcon;
//
UILabel *tollLabel = [[UILabel alloc] init];
tollLabel.font = headTextFont;
tollLabel.textColor = headTextColor;
[card addSubview:tollLabel];
self.tollLabel = tollLabel;
// &
UIImageView *personIcon = [[UIImageView alloc] init];
personIcon.contentMode = UIViewContentModeScaleAspectFit;
personIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_person"];
[card addSubview:personIcon];
self.contactPersonIconView = personIcon;
UILabel *personLabel = [[UILabel alloc] init];
personLabel.font = headTextFont;
personLabel.textColor = headTextColor;
[card addSubview:personLabel];
self.contactPersonLabel = personLabel;
// &
UIImageView *priceIcon = [[UIImageView alloc] init];
priceIcon.contentMode = UIViewContentModeScaleAspectFit;
priceIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_price"];
[card addSubview:priceIcon];
self.priceIconView = priceIcon;
UILabel *priceLabel = [[UILabel alloc] init];
priceLabel.font = headTextFont;
priceLabel.textColor = headTextColor;
[card addSubview:priceLabel];
self.priceLabel = priceLabel;
// &
UIImageView *phoneIcon = [[UIImageView alloc] init];
phoneIcon.contentMode = UIViewContentModeScaleAspectFit;
phoneIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_phone"];
[card addSubview:phoneIcon];
self.phoneIconView = phoneIcon;
UILabel *phoneLabel = [[UILabel alloc] init];
phoneLabel.font = headTextFont;
phoneLabel.textColor = headTextColor;
[card addSubview:phoneLabel];
self.phoneLabel = phoneLabel;
//
UIButton *naviBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[naviBtn setTitle:@"开始导航" forState:UIControlStateNormal];
[naviBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
naviBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
naviBtn.backgroundColor = AStationThemeGreen();
naviBtn.layer.cornerRadius = 24;
[naviBtn addTarget:self action:@selector(_onStartNaviTapped) forControlEvents:UIControlEventTouchUpInside];
[card addSubview:naviBtn];
self.startNaviButton = naviBtn;
}
#pragma mark - Masonry Constraints
- (void)_setupMasonryConstraints {
UIView *card = self.cardView;
CGFloat iconSize = 16;
//
[self.maskControl mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
// 16 safeArea 30pt
// top bottom
[card mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.right.equalTo(self.view).offset(-16);
// =
make.top.equalTo(self.view.mas_bottom).offset(0);
}];
//
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(card).offset(8);
make.right.equalTo(card).offset(-15);
make.width.height.mas_equalTo(40);
}];
// + nameLabel20pt
// nameLabel55%
[self.stationNameLabel setContentHuggingPriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisHorizontal];
[self.stationNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(card).offset(25);
make.left.equalTo(card).offset(16);
// make.width.lessThanOrEqualTo(card).multipliedBy(0.45);
make.right.equalTo(self.closeButton.mas_left).offset(-12);
}];
// 6pt
[self.businessHoursLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.stationNameLabel.mas_bottom).offset(10);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
}];
// 6pt
[self.addressLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.businessHoursLabel.mas_bottom).offset(10);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
}];
self.separator.hidden = YES;
// 线12pt
[self.separator mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.addressLabel.mas_bottom).offset(10);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(0.5);
}];
CGFloat _offset_y = 18;
// 线14pt
[self.timeIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.separator.mas_bottom).offset(12);
make.left.equalTo(card).offset(16);
make.width.height.mas_equalTo(iconSize);
}];
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.timeIconView);
make.left.equalTo(self.timeIconView.mas_right).offset(6);
// make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(24);
}];
///cost
[self.costIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.timeIconView);
make.left.equalTo(self.timeLabel.mas_right).offset(40);
make.width.height.mas_equalTo(iconSize);
}];
[self.costLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.costIconView);
make.left.equalTo(self.costIconView.mas_right).offset(6);
make.right.lessThanOrEqualTo(card).offset(-10);
}];
// + 12pt
[self.distanceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.timeIconView.mas_bottom).offset(_offset_y);
make.left.equalTo(card).offset(16);
make.width.height.mas_equalTo(iconSize);
}];
[self.distanceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.distanceIconView);
make.left.equalTo(self.distanceIconView.mas_right).offset(6);
//
make.width.mas_lessThanOrEqualTo(130);
make.height.mas_equalTo(24);
}];
[self.tollIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.distanceIconView);
make.left.equalTo(self.costIconView.mas_left);
make.width.height.mas_equalTo(iconSize);
}];
[self.tollLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.distanceIconView);
make.left.equalTo(self.tollIconView.mas_right).offset(6);
// make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(24);
}];
// 14pt
[self.contactPersonIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.distanceIconView.mas_bottom).offset(_offset_y);
make.left.equalTo(card).offset(16);
make.width.height.mas_equalTo(iconSize);
}];
[self.contactPersonLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contactPersonIconView);
make.left.equalTo(self.contactPersonIconView.mas_right).offset(6);
make.height.mas_equalTo(24);
}];
// costIconView
[self.priceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contactPersonIconView);
make.left.equalTo(self.costIconView.mas_left);
make.width.height.mas_equalTo(iconSize);
}];
[self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.contactPersonIconView);
make.left.equalTo(self.priceIconView.mas_right).offset(6);
make.right.lessThanOrEqualTo(card).offset(-10);
make.height.mas_equalTo(24);
}];
// 12pt
[self.phoneIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.contactPersonIconView.mas_bottom).offset(_offset_y);
make.left.equalTo(card).offset(16);
make.width.height.mas_equalTo(iconSize);
}];
[self.phoneLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.phoneIconView);
make.left.equalTo(self.phoneIconView.mas_right).offset(6);
make.height.mas_equalTo(24);
}];
// 18pt safeArea
[self.startNaviButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.phoneIconView.mas_bottom).offset(40);
make.left.equalTo(card).offset(16);
make.right.equalTo(card).offset(-16);
make.height.mas_equalTo(48);
make.bottom.equalTo(card).offset(-AMP_TabbarSafeBottomMargin); // 30pt
}];
}
#pragma mark - Data Update
- (void)_updateUI {
self.stationNameLabel.text = (self.pointModel.name.length > 0)
? self.pointModel.name : @"-";
//
self.businessHoursLabel.text = (self.businessHours.length > 0)
? [NSString stringWithFormat:@"营业时间:%@", self.businessHours]
: @"营业时间:-";
self.costLabel.text = (self.estimatedCost.length > 0)
? [NSString stringWithFormat:@"预计加氢费用:%@元", self.estimatedCost]
: @"预计加氢费用:-";
self.addressLabel.text = (self.pointModel.address.length > 0)
? self.pointModel.address : @"-";
// "-- 分钟"
self.timeLabel.text = (self.estimatedTime.length > 0)
? [NSString stringWithFormat:@"预计时间:%@分钟", self.estimatedTime]
: @"预计时间:-";
// "-- 公里"
self.distanceLabel.text = (self.driveDistance.length > 0)
? [NSString stringWithFormat:@"行驶里程:%@公里", self.driveDistance]
: @"行驶里程:-";
// "-- 元"
self.tollLabel.text = (self.tollFee.length > 0)
? [NSString stringWithFormat:@"过路费:%@元", self.tollFee]
: @"过路费:-";
// -
self.contactPersonLabel.text = (self.contactName.length > 0)
? [NSString stringWithFormat:@"站联系人:%@", self.contactName]
: @"站联系人:-";
// -
self.priceLabel.text = (self.hydrogenPrice.length > 0)
? [NSString stringWithFormat:@"加氢价格:%@/L", self.hydrogenPrice]
: @"加氢价格:-";
// -
self.phoneLabel.text = (self.contactPhone.length > 0)
? [NSString stringWithFormat:@"联系方式:%@", self.contactPhone]
: @"联系方式:-";
}
// UI
-(void)resetUI {
self.stationNameLabel.text = @"-";
self.businessHoursLabel.text = @"营业时间:-";
self.costLabel.text = @"预计加氢费用:-";
self.addressLabel.text = @"地址:-";
self.timeLabel.text = @"预计时间:-";
self.distanceLabel.text = @"行驶里程:-";
self.tollLabel.text = @"过路费:-";
self.contactPersonLabel.text = @"站联系人:-";
self.priceLabel.text = @"加氢价格:-";
self.phoneLabel.text = @"联系方式:-";
}
#pragma mark - Animation
/**
top 30pt safeArea
*/
- (void)_playShowAnimation {
// safeArea 30pt
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(0);
make.right.equalTo(self.view).offset(-0);
make.bottom.equalTo(self.view).offset(0);
}];
self.maskControl.alpha = 0;
[UIView animateWithDuration:0.36
delay:0
usingSpringWithDamping:0.82
initialSpringVelocity:0.5
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.maskControl.alpha = 1;
[self.view layoutIfNeeded];
} completion:nil];
}
- (void)_playDismissAnimationWithCompletion:(void(^)(void))completion {
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.right.equalTo(self.view).offset(-16);
make.top.equalTo(self.view.mas_bottom).offset(20);
}];
[UIView animateWithDuration:0.25
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.maskControl.alpha = 0;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
if (completion) completion();
}];
}
#pragma mark - Actions
- (void)_onMaskTapped {
if ([self.delegate respondsToSelector:@selector(stationDetailPopupDidTapClose:)]) {
[self.delegate stationDetailPopupDidTapClose:self];
}
[self dismiss];
}
- (void)_onCloseTapped {
if ([self.delegate respondsToSelector:@selector(stationDetailPopupDidTapClose:)]) {
[self.delegate stationDetailPopupDidTapClose:self];
}
[self dismiss];
}
- (void)_onStartNaviTapped {
// delegate
// presentViewController
__weak typeof(self) weakSelf = self;
[self dismissWithCompletion:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if ([strongSelf.delegate respondsToSelector:@selector(stationDetailPopupDidTapStartNavi:)]) {
[strongSelf.delegate stationDetailPopupDidTapStartNavi:strongSelf];
}
}];
}
@end

View File

@@ -0,0 +1,67 @@
//
// AAlgorithmPathModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
NS_ASSUME_NONNULL_BEGIN
@interface AAlgorithmPathModel : NSObject
/// 单程
@property (nonatomic, copy, nullable) NSString *tripRecommendationId;
@property (nonatomic, copy, nullable) NSString *staId;
@property (nonatomic, copy, nullable) NSString *tripOneWayPathId;
@property (nonatomic, copy, nullable) NSString *tripReturnPathId;
/// 里程(公里)
@property (nonatomic, copy, nullable) NSString *oneWayDis;
@property (nonatomic, copy, nullable) NSString *returnDis;
@property (nonatomic, copy, nullable) NSString *roundTripDis;
/// 时长(分钟)
@property (nonatomic, copy, nullable) NSString *oneWayTime;
@property (nonatomic, copy, nullable) NSString *returnTime;
@property (nonatomic, copy, nullable) NSString *roundTripTime;
/// 总成本(元)
@property (nonatomic, copy, nullable) NSString *oneWayCost;
@property (nonatomic, copy, nullable) NSString *returnCost;
@property (nonatomic, copy, nullable) NSString *roundTripCost;
/// 人工成本(元)
@property (nonatomic, copy, nullable) NSString *oneWayLaborCost;
@property (nonatomic, copy, nullable) NSString *returnLaborCost;
@property (nonatomic, copy, nullable) NSString *roundTripLaborCost;
/// 高速成本(元)
@property (nonatomic, copy, nullable) NSString *oneWayChargerouteCost;
@property (nonatomic, copy, nullable) NSString *returnChargerouteCost;
@property (nonatomic, copy, nullable) NSString *roundTripChargerouteCost;
/// 氢耗(公斤)
@property (nonatomic, copy, nullable) NSString *oneWayHydrogenConsumption;
@property (nonatomic, copy, nullable) NSString *returnLaborHydrogenConsumption;
@property (nonatomic, copy, nullable) NSString *roundTripHydrogenConsumption;
/// 氢气成本(元)
@property (nonatomic, copy, nullable) NSString *oneWayHydrogenCost;
@property (nonatomic, copy, nullable) NSString *returnLaborHydrogenCost;
@property (nonatomic, copy, nullable) NSString *roundTripHydrogenCost;
/// 加氢站相关
@property (nonatomic, copy, nullable) NSString *hydrogenCost; // 氢气总成本(元)
@property (nonatomic, copy, nullable) NSString *hydrogenStaServiceTime; // 站服务总时长(分钟)
@property (nonatomic, copy, nullable) NSString *hydrogenStaRefuelingTime;// 实际加油时长(分钟)
@property (nonatomic, copy, nullable) NSString *hydrogenStaQueueTime; // 排队时长(分钟)
@property (nonatomic, copy, nullable) NSString *hydrogenStaServiceTimeCost; // 站服务时间成本(元)
@property (nonatomic, copy, nullable) NSString *hydrogenStaRefuelingTimeCost;// 加油时间成本(元)
@property (nonatomic, copy, nullable) NSString *hydrogenStaQueueTimeCost; // 排队时间成本(元)
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,12 @@
//
// AAlgorithmPathModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "AAlgorithmPathModel.h"
@implementation AAlgorithmPathModel
@end

View File

@@ -0,0 +1,17 @@
//
// ACustomPointAnnotation.h
// Pods
//
// Created by admin on 2026/3/25.
//
#import <AMapNaviKit/AMapNaviKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ACustomPointAnnotation : MAPointAnnotation
@property (nonatomic, copy, nullable) NSString *stationID;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,12 @@
//
// ACustomPointAnnotation.m
// Pods
//
// Created by admin on 2026/3/25.
//
#import "ACustomPointAnnotation.h"
@implementation ACustomPointAnnotation
@end

View File

@@ -0,0 +1,81 @@
//
// AMapHyStationModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/2/11.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
NS_ASSUME_NONNULL_BEGIN
/**
{
"name": "嘉兴经开站",
"shortName": null,
"siteNo": null,
"city": null,
"address": "嘉兴市秀洲区岗山路272号",
"contact": "龚明伟",
"phone": "18888888888",
"type": null,
"coOpMode": null,
"booking": null,
"siteStatus": 0,
"startBusiness": "06:00:00",
"endBusiness": "22:00:00",
"billingMethod": null,
"term": null,
"remark": null,
"longitude": "120.75972800",
"latitude": "30.79962800"
}
*/
@interface AMapHyStationModel : NSObject
@property (nonatomic, copy, nullable) NSString *ID;
@property (nonatomic, copy, nullable) NSString *hydrogenId;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy, nullable) NSString *shortName;
@property (nonatomic, copy, nullable) NSString *siteNo;
@property (nonatomic, copy, nullable) NSString *city;
@property (nonatomic, copy, nullable) NSString *address;
@property (nonatomic, copy, nullable) NSString *contact;
@property (nonatomic, copy, nullable) NSString *phone;
@property (nonatomic, copy, nullable) NSString *type;
@property (nonatomic, copy, nullable) NSString *coOpMode;
@property (nonatomic, strong, nullable) NSString * booking;
@property (nonatomic, assign) NSInteger siteStatus;
@property (nonatomic, copy, nullable) NSString *startBusiness;
@property (nonatomic, copy, nullable) NSString *endBusiness;
@property (nonatomic, copy, nullable) NSString *billingMethod;
@property (nonatomic, copy, nullable) NSString *term;
@property (nonatomic, copy, nullable) NSString *remark;
@property (nonatomic, copy, nullable) NSString *longitude;
@property (nonatomic, copy, nullable) NSString *latitude;
@end
/**
{
"code": 0,
"status": true,
"message": "success",
"data": [],
"time": "1770800256408",
"error": null
}
*/
@interface AMapHyResponse : NSObject
@property (nonatomic, assign) NSInteger code;
@property (nonatomic, assign) NSInteger status;
@property (nonatomic, copy, nullable) NSString *message;
@property (nonatomic, copy, nullable) NSString *time;
@property (nonatomic, copy, nullable) NSString *error;
@property(nonatomic , strong)NSArray <AMapHyStationModel * > * data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,25 @@
//
// AMapHyStationModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/2/11.
//
#import "AMapHyStationModel.h"
@implementation AMapHyStationModel
+ (NSDictionary *)mj_replacedKeyFromPropertyName
{
return @{ @"ID" : @"id"};
}
@end
@implementation AMapHyResponse
+ (NSDictionary *)mj_objectClassInArray {
return @{@"data" : AMapHyStationModel.class};
}
@end

View File

@@ -0,0 +1,26 @@
//
// ANavPointModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ANavPointModel : NSObject
@property (nonatomic, copy, nullable) NSString *name;
@property (nonatomic, copy, nullable) NSString *address;
@property (nonatomic, copy, nullable) NSString *stationID;
///经纬度
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
///初始化
+ (instancetype)instanceWithCoordinate:(CLLocationCoordinate2D)coordinate name:(NSString *)name address:(NSString *)address;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,22 @@
//
// ANavPointModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "ANavPointModel.h"
@implementation ANavPointModel
///
+ (instancetype)instanceWithCoordinate:(CLLocationCoordinate2D)coordinate name:(NSString *)name address:(NSString *)address {
ANavPointModel *instance = [[ANavPointModel alloc] init];
if (instance) {
instance.coordinate = coordinate;
instance.name = name;
instance.address = address;
}
return instance;
}
@end

View File

@@ -0,0 +1,32 @@
//
// ANaviPathInfoModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
NS_ASSUME_NONNULL_BEGIN
/**
{
"name": "浙江省嘉兴市平湖市乍浦镇滨海大道中国石化滨海大道加油加气站",
"poiId": "",
"coordinate": {
"longitude": "121.070434",
"latitude": "30.596124"
}
}
*/
@interface ANaviPathInfoModel : NSObject
@property (nonatomic, copy, nullable) NSString *name;
@property (nonatomic, copy, nullable) NSString *poiId;
@property (nonatomic, copy, nullable) NSString *longitude;
@property (nonatomic, copy, nullable) NSString *latitude;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,20 @@
//
// ANaviPathInfoModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "ANaviPathInfoModel.h"
@implementation ANaviPathInfoModel
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
// JSON coordinate longitude/latitude
return @{
@"longitude": @"coordinate.longitude",
@"latitude": @"coordinate.latitude"
};
}
@end

View File

@@ -0,0 +1,29 @@
//
// APathModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
#import "ANaviPathInfoModel.h"
NS_ASSUME_NONNULL_BEGIN
@class ANaviPathInfoModel;
@interface APathModel : NSObject
@property (nonatomic, assign) CGFloat distance; // 总距离(米)
@property (nonatomic, assign) CGFloat duration; // 总时长(秒)
@property (nonatomic, copy, nullable) NSString *strategy; // 路线策略,如"避免拥堵"
@property (nonatomic, copy, nullable) NSString *tolls; // 高速费(元)
@property (nonatomic, assign) CGFloat toll_distance; // 高速里程(米)
@property (nonatomic, assign) NSInteger restriction; // 限行标志,-1 无
@property (nonatomic, assign) NSInteger traffic_lights; // 红绿灯数量
@property (nonatomic, strong) NSArray<ANaviPathInfoModel *> *naviList; // 途经点列表
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,17 @@
//
// APathModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "APathModel.h"
#import "ANaviPathInfoModel.h"
@implementation APathModel
+ (NSDictionary *)mj_objectClassInArray {
return @{@"naviList": [ANaviPathInfoModel class]};
}
@end

View File

@@ -0,0 +1,69 @@
//
// ASiteModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
NS_ASSUME_NONNULL_BEGIN
/**
{
"innerSiteId": "202304241822210001",
"name": "滨海大道加油加气站",
"shortName": "滨海",
"siteNo": "000001",
"city": "浙江省-嘉兴市",
"address": "嘉兴市平湖市滨海大道1515号",
"contact": "陆平",
"phone": "18666666666",
"type": "",
"coOpMode": "签约",
"booking": "无需预约",
"siteStatus": "0",
"siteStatusName": "营运中",
"startBusiness": "08:00:00",
"endBusiness": "20:00:00",
"billingMethod": "月付款",
"term": "1703952000000",
"remark": "",
"longitude": "121.07112700",
"latitude": "30.59577700",
"distance": ""
}
*/
@interface ASiteModel : NSObject
@property (nonatomic, copy, nullable) NSString *innerSiteId;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy, nullable) NSString *shortName;
@property (nonatomic, copy, nullable) NSString *siteNo;
@property (nonatomic, copy, nullable) NSString *city;
@property (nonatomic, copy, nullable) NSString *address;
@property (nonatomic, copy, nullable) NSString *contact;
@property (nonatomic, copy, nullable) NSString *phone;
@property (nonatomic, copy, nullable) NSString *type;
@property (nonatomic, copy, nullable) NSString *coOpMode;
@property (nonatomic, copy, nullable) NSString *booking;
@property (nonatomic, copy, nullable) NSString *siteStatus;
@property (nonatomic, copy, nullable) NSString *siteStatusName;
@property (nonatomic, copy, nullable) NSString *startBusiness;
@property (nonatomic, copy, nullable) NSString *endBusiness;
@property (nonatomic, copy, nullable) NSString *billingMethod;
@property (nonatomic, copy, nullable) NSString *term;
@property (nonatomic, copy, nullable) NSString *remark;
@property (nonatomic, copy, nullable) NSString *longitude;
@property (nonatomic, copy, nullable) NSString *latitude;
@property (nonatomic, copy, nullable) NSString *distance;
///新增弹框参数4.13
@property (nonatomic, copy, nullable) NSString *hydrogenPrice;
@property (nonatomic, copy, nullable) NSString *liaisonName;
@property (nonatomic, copy, nullable) NSString *liaisonPhone;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,12 @@
//
// ASiteModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "ASiteModel.h"
@implementation ASiteModel
@end

View File

@@ -0,0 +1,65 @@
//
// ATripCalcResponse.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
#import "AAlgorithmPathModel.h"
#import "APathModel.h"
#import "ASiteModel.h"
#import "ANaviPathInfoModel.h"
#import "ATruckModel.h"
NS_ASSUME_NONNULL_BEGIN
@class ATruckModel;
@class ASiteModel;
@class APathModel;
@class AAlgorithmPathModel;
#pragma mark - data 节点
/**
{
"truckDto": { ... },
"truckDtoStr": null,
"destinationSite": { ... },
"pathDto": { ... },
"algorithmPath": { ... },
"isInvokeAlgorithm": true
}
*/
@interface ATripCalcDataModel : NSObject
@property (nonatomic, strong, nullable) ATruckModel *truckDto;
@property (nonatomic, copy, nullable) NSString *truckDtoStr;
@property (nonatomic, strong, nullable) ASiteModel *destinationSite;
@property (nonatomic, strong, nullable) APathModel *pathDto;
@property (nonatomic, strong, nullable) AAlgorithmPathModel *algorithmPath;
@property (nonatomic, assign) BOOL isInvokeAlgorithm;
@end
#pragma mark - 根响应
/**
{
"code": 200,
"msg": "操作成功!",
"data": { ... }
}
*/
@interface ATripCalcResponse : NSObject
@property (nonatomic, assign) NSInteger code;
@property (nonatomic, copy, nullable) NSString *msg;
@property (nonatomic, strong, nullable) ATripCalcDataModel *data;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
//
// ATripCalcResponse.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "ATripCalcResponse.h"
#import "ATruckModel.h"
#import "ASiteModel.h"
#import "APathModel.h"
#import "AAlgorithmPathModel.h"
@implementation ATripCalcDataModel
+ (NSDictionary *)mj_objectClassInArray {
return @{
@"truckDto": [ATruckModel class],
@"destinationSite": [ASiteModel class],
@"pathDto": [APathModel class],
@"algorithmPath": [AAlgorithmPathModel class]
};
}
@end
@implementation ATripCalcResponse
+ (NSDictionary *)mj_objectClassInArray {
return @{@"data":[ATripCalcDataModel class]};
}
@end

View File

@@ -0,0 +1,58 @@
//
// ATruckModel.h
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import <Foundation/Foundation.h>
#import <MJExtension/MJExtension.h>
NS_ASSUME_NONNULL_BEGIN
/**
{
"isRestriction": true,
"mvehicleSizeName": "重型货车",
"mvehicleAxisUnit": "轴",
"mcarNumber": "浙F32111F",
"mcarType": 0,
"mvehicleHeight": "3.8",
"mvehicleHeightUnit": "M",
"mvehicleWeight": null,
"mvehicleWeightUnit": "T",
"mvehicleLoad": "49.0",
"mvehicleLoadUnit": "T",
"mvehicleLoadSwitch": false,
"mvehicleWidth": "0.0",
"mvehicleWidthUnit": "M",
"mvehicleLength": "7.6",
"mvehicleLengthUnit": "M",
"mvehicleSize": 4,
"mvehicleAxis": 5
}
*/
@interface ATruckModel : NSObject
@property (nonatomic, assign) BOOL isRestriction;
@property (nonatomic, copy, nullable) NSString *mvehicleSizeName;
@property (nonatomic, copy, nullable) NSString *mvehicleAxisUnit;
@property (nonatomic, copy, nullable) NSString *mcarNumber;
@property (nonatomic, assign) NSInteger mcarType;
@property (nonatomic, copy, nullable) NSString *mvehicleHeight;
@property (nonatomic, copy, nullable) NSString *mvehicleHeightUnit;
@property (nonatomic, copy, nullable) NSString *mvehicleWeight;
@property (nonatomic, copy, nullable) NSString *mvehicleWeightUnit;
@property (nonatomic, copy, nullable) NSString *mvehicleLoad;
@property (nonatomic, copy, nullable) NSString *mvehicleLoadUnit;
@property (nonatomic, assign) BOOL mvehicleLoadSwitch;
@property (nonatomic, copy, nullable) NSString *mvehicleWidth;
@property (nonatomic, copy, nullable) NSString *mvehicleWidthUnit;
@property (nonatomic, copy, nullable) NSString *mvehicleLength;
@property (nonatomic, copy, nullable) NSString *mvehicleLengthUnit;
@property (nonatomic, assign) NSInteger mvehicleSize;
@property (nonatomic, assign) NSInteger mvehicleAxis;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,12 @@
//
// ATruckModel.m
// AMapNavIOSSDK
//
// Created by admin on 2026/3/25.
//
#import "ATruckModel.h"
@implementation ATruckModel
@end

View File

@@ -0,0 +1,43 @@
//
// AMapNavCommonUtil.h
// Pods
//
// Created by admin on 2026/2/11.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UIColor+ANavMap.h"
#import "UIFont+HP.h"
NS_ASSUME_NONNULL_BEGIN
@interface AMapNavCommonUtil : NSObject
/// 显示加载转圈nil 消息时不显示文字)
+ (void)showLoadingWithMsg:(nullable NSString *)msg;
/// 关闭所有 MBProgressHUD
+ (void)dismiss;
/// 显示提示,自动 2.0s 后消失
+ (void)showMsg:(NSString *)msg;
/// 获取图片2x
+ (UIImage *)imageWithName:(NSString *)name;
/// 获取图片3x
+ (UIImage *)imageWithName3x:(NSString *)name;
/// 判断字符串是否为空
BOOL stringIsEmpty(NSString *str);
/// 判断字符串是否非空
BOOL stringIsNotEmpty(NSString *str);
+(NSString *)stringValueFromStr:(NSString *)str;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,153 @@
//
// AMapNavCommonUtil.m
// Pods
//
// Created by admin on 2026/2/11.
//
#import "AMapNavCommonUtil.h"
#import <MBProgressHUD/MBProgressHUD.h>
/// HUD
static MBProgressHUD *_sharedHUD = nil;
@implementation AMapNavCommonUtil
#pragma mark - MBProgressHUD
+ (UIWindow *)_keyWindow {
if (@available(iOS 13.0, *)) {
for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
for (UIWindow *window in scene.windows) {
if (window.isKeyWindow) return window;
}
}
}
}
return [UIApplication sharedApplication].keyWindow;
}
+ (void)showLoadingWithMsg:(NSString *)msg {
dispatch_async(dispatch_get_main_queue(), ^{
// [self dismiss];
UIWindow *window = [self _keyWindow];
if (!window) return;
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.bezelView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.75];
hud.contentColor = [UIColor whiteColor];
if (msg.length > 0) {
hud.label.text = msg;
hud.label.font = [UIFont systemFontOfSize:14];
}
hud.removeFromSuperViewOnHide = YES;
_sharedHUD = hud;
});
}
+ (void)dismiss {
dispatch_async(dispatch_get_main_queue(), ^{
if (_sharedHUD) {
[_sharedHUD hideAnimated:YES];
_sharedHUD = nil;
} else {
// HUD
UIWindow *window = [self _keyWindow];
if (window) {
[MBProgressHUD hideHUDForView:window animated:YES];
}
}
});
}
+ (void)showMsg:(NSString *)msg {
if (msg.length == 0) return;
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow *window = [self _keyWindow];
if (!window) return;
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.mode = MBProgressHUDModeText;
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
hud.bezelView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.75];
hud.contentColor = [UIColor whiteColor];
hud.label.text = msg;
hud.label.font = [UIFont systemFontOfSize:14];
hud.removeFromSuperViewOnHide = YES;
// 2.0s
[hud hideAnimated:YES afterDelay:2.0];
});
}
#pragma mark -
BOOL stringIsEmpty (NSString *str)
{
if (str == nil || str == NULL)
{
return YES;
}
if ([str isKindOfClass:[NSNull class]])
{
return YES;
}
if ([str isKindOfClass:[NSString class]])
{
NSString * newStr = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSSet *emptySet = [NSSet setWithObjects:@"", @"null", @"(null)", @"<null>", @"NULL", @"无",@"kZero", nil];
if ([emptySet containsObject:str] || [emptySet containsObject:newStr]) {
return YES;
} else {
return [newStr length] == 0;
}
}
return NO;
}
BOOL stringIsNotEmpty (NSString *str)
{
return ! stringIsEmpty(str);
}
///
+(NSString *)stringValueFromStr:(NSString *)str {
if (stringIsEmpty(str)) {
return @"";
}
return str;
}
#pragma mark -
+(UIImage *)imageWithName:(NSString *)name {
NSURL * url = [[NSBundle mainBundle] URLForResource:@"AMapNavIOSSDK" withExtension:@"bundle"];
NSBundle *containnerBundle = [NSBundle bundleWithURL:url];
NSString * path = [containnerBundle pathForResource:[NSString stringWithFormat:@"%@@2x.png" , name] ofType:nil];
UIImage * arrowImage = [[UIImage imageWithContentsOfFile:path] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
return arrowImage;
}
+(UIImage *)imageWithName3x:(NSString *)name {
NSURL * url = [[NSBundle mainBundle] URLForResource:@"AMapNavIOSSDK" withExtension:@"bundle"];
NSBundle *containnerBundle = [NSBundle bundleWithURL:url];
NSString * path = [containnerBundle pathForResource:[NSString stringWithFormat:@"%@@3x.png" , name] ofType:nil];
UIImage * arrowImage = [[UIImage imageWithContentsOfFile:path] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
return arrowImage;
}
@end

View File

@@ -0,0 +1,19 @@
//
// AMapNavHttpUtil.h
// AMapNavIOSSDK
//
// Created by admin on 2026/2/11.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface AMapNavHttpUtil : NSObject
+ (void)postRequestWithURL:(NSString *)urlString parameters:(id)parameters requestHeader:(NSDictionary *)headParam successHandler:(void (^)(NSDictionary *data, NSURLResponse *response))successHandler failureHandler:(void ( ^)(NSError *error))failureHandler;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More