创建 iOS 应用程序 - Amazon Location Service

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

创建 iOS 应用程序

在本节中,您将创建一个能够在前台进行位置搜索和跟踪的 iOS 应用程序。首先,您将创建您的亚马逊位置资源,并为您的应用程序创建一个 Amazon Cognito 身份。

为您的应用程序创建 Amazon Location 资源

如果您还没有,则必须创建应用程序将使用的 Amazon Location 资源。您将创建一个用于在应用程序中显示地图的地图资源、一个用于在地图上搜索位置的地点索引以及一个用于在地图上跟踪对象的跟踪器。

向您的应用程序添加位置资源
  1. 选择要使用的地图样式。

    1. 在 Amazon Location 控制台的地图页面上,选择创建地图以预览地图样式。

    2. 为新地图资源添加名称描述。记下您用于地图资源的名称。在本教程的后面部分创建脚本文件时,您将需要它。

    3. 选择一张地图。

      注意

      选择地图样式还会选择要使用的地图数据提供程序。如果您的应用程序正在跟踪或路由您在企业中使用的资产,例如运载车辆或员工,则只能使用 HERE 作为地理位置提供程序。想要了解更多信息,请参阅 AWS 服务条款的第 82 节。

    4. 同意 Amazon Location 条款和条件,然后选择创建地图。您可以与所选地图进行交互:放大、缩小或向任意方向平移。

    5. 请记下新地图资源显示的 Amazon 资源名称 (ARN)。在本教程的后面部分,您将使用它来创建正确的身份验证。

  2. 选择要使用的地点索引。

    1. 在 Amazon Location 控制台的地点索引页面上,选择创建地点索引

    2. 为新的地点索引资源添加名称描述。记下地点索引资源使用的名称。在本教程的后面部分创建脚本文件时,您将需要它。

    3. 选择数据提供程序。

      注意

      在大多数情况下,请选择与您已选择的地图提供程序相匹配的数据提供程序。这有助于确保搜索结果与地图相匹配。

      如果您的应用程序正在跟踪或路由您在企业中使用的资产,例如运载车辆或员工,则只能使用 HERE 作为地理位置提供程序。想要了解更多信息,请参阅 AWS 服务条款的第 82 节。

    4. 选择数据存储选项。在本教程中,不存储结果,因此您可以选择否,仅限一次性使用

    5. 同意 Amazon Location 条款和条件,然后选择创建地点索引

    6. 记下为您的新地点索引资源显示的 ARN。在本教程的下一部分,您将使用它来创建正确的身份验证。

  3. 使用 Amazon Location 控制台创建追踪器。

    1. 打开 Amazon Location Service 控制台

    2. 在左侧导航窗格中,选择跟踪器

    3. 选择创建跟踪器

    4. 填写所有必填字段。

    5. 在 “位置筛选” 下,我们建议您使用默认设置:TimeBased

    6. 选择 “创建跟踪链接” 以完成操作。

为应用程序设置身份验证

您在本教程中创建的应用程序是匿名使用的,这意味着您的用户无需登录 AWS 即可使用该应用程序。但是,Amazon Location Service API 需要身份验证才能使用。您将使用 Amazon Cognito 为匿名用户提供身份验证和授权。本教程将使用 Amazon Cognito 对您的应用程序进行身份验证。

注意

有关将 Amazon Cognito 与 Amazon Location Service 配合使用的更多信息,请参阅。授予对 Amazon Location Service 的访问权限

以下教程向您展示如何为在中创建的地图、地点索引和追踪器设置身份验证,以及如何为 Amazon Location 设置权限。

创建用于跟踪的 IAM 策略
  1. 使用您具有管理员权限的账户,通过以下网址登录到 IAM 控制台:https://console.aws.amazon.com/iam/。

  2. 在导航窗格中,选择策略。

  3. 在内容窗格中,选择创建策略。

  4. 选择 JSON 选项,然后将此 JSON 策略复制并粘贴到 JSON 文本框中。

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "geo:GetMapTile", "geo:GetMapStyleDescriptor", "geo:GetMapSprites", "geo:GetMapGlyphs", "geo:SearchPlaceIndexForPosition", "geo:GetDevicePositionHistory", "geo:BatchUpdateDevicePosition" ], "Resource": [ "arn:aws:geo:{Region}:{Account}:map/{MapName}", "arn:aws:geo:{Region}:{Account}:place-index/{IndexName}", "arn:aws:geo:{Region}:{Account}:tracker/{TrackerName}" ] } ] }

    这是跟踪的政策示例。要将示例用于您自己的策略,请替换RegionAccountIndexNameMapNameTrackerName占位符。

    注意

    虽然未经身份验证的身份池旨在在不安全的互联网站点上公开,但请注意,它们将被交换为标准的、有时间限制 AWS 的凭证。

    适当确定与未经身份验证的身份池关联的 IAM 角色的范围很重要。有关在亚马逊定位服务的 Amazon Cognito 中使用策略和适当确定政策范围的更多信息,请参阅授予亚马逊定位服务访问权限

  5. 在 “查看并创建” 页面上,为策略名称字段提供一个名称。查看您的策略授予的权限,然后选择创建策略以保存您所做的工作。

将在托管策略列表中显示新策略,并已准备好附加该策略。

为追踪设置身份验证
  1. Amazon Cognito 控制台中为您的地图应用程序设置身份验证。

  2. 打开身份池页面。

    注意

    您创建的资源池必须与您在上一节中创建的 Amazon Location Service 资源位于相同的 AWS 账户和 AWS 区域。

  3. 选择创建身份池

  4. 配置身份池信任步骤开始。要进行用户访问身份验证,请选择访客访问权限,然后按下一步。

  5. 配置权限页面上,选择使用现有 IAM 角色并输入您在上一步中创建的 IAM 角色的名称。准备就绪后,按下一步继续下一步。

  6. 配置属性页面上,为您的身份池提供一个名称。然后按 “下一步”。

  7. 在 “查看并创建” 页面上,查看所有存在的信息,然后按创建身份池

  8. 打开身份池页面,然后选择您刚刚创建的身份池。然后复制或写下 IdentityPoolId 稍后将在浏览器脚本中使用的内容。

创建基本 iOS 应用程序

在本教程中,您将创建一个嵌入地图的 iOS 应用程序,并允许用户查找地图上某个位置的内容。

首先,让我们使用 Xcode 的项目向导创建一个 Swift 应用程序。

创建空应用程序 (Xcode)
  1. 打开 Xcode,然后从菜单中选择 “文件”、“新建”、“建项目”。

  2. iOS 选项卡中,选择 “应用程序”,然后选择 “下一步”。

  3. 提供产品名称组织标识符,然后在 “接口” 字段中输入SwiftUI。选择 “下一步” 以完成选择。

  4. 选择要保存项目的位置,然后按创建按钮创建空应用程序。

创建基础应用程序后,您需要为示例应用程序安装所需的软件包。

安装所需的依赖项
  1. 在 Xcode 中,右键单击项目并选择 “添加包...” 。这将打开 “包” 窗口,您可以在其中向项目中添加包。

  2. 在 “包” 窗口中,添加以下软件包:

设置初始代码

在您的应用程序中启用位置权限
  1. 打开你的 Xcode 项目。

  2. 找到项目的Info.plist文件。

  3. 根据您的应用程序要求添加必要的位置权限密钥。以下是钥匙:

    • NSLocationWhenInUseUsageDescription:描述为什么您的应用程序在使用时需要位置访问权限。

    • NSLocationAlwaysAndWhenInUseUsageDescription:描述为什么您的应用需要持续的位置访问权限。

现在,您需要在应用程序中配置资源值。添加一个名为的新文件Config.xcconfig并填写您之前在 Amazon 控制台中创建的值。

REGION = INDEX_NAME = MAP_NAME = IDENTITY_POOL_ID = TRACKER_NAME =
  1. 从左侧的导航器部分中,选择项目。

  2. 在 “目标” 部分下,选择您的应用程序,然后单击 “信息” 选项卡。

  3. 添加信息属性,其值如下所示:

  4. 添加包含以下内容的Config.swift文件,该文件将从 Bundle 信息文件中读取配置值。

    import Foundation enum Config { static let region = Bundle.main.object(forInfoDictionaryKey: "Region") as! String static let mapName = Bundle.main.object(forInfoDictionaryKey: "MapName") as! String static let indexName = Bundle.main.object(forInfoDictionaryKey: "IndexName") as! String static let identityPoolId = Bundle.main.object(forInfoDictionaryKey: "IdentityPoolId") as! String static let trackerName = Bundle.main.object(forInfoDictionaryKey: "TrackerName") as! String }
  5. 使用名称创建一个新文件夹,ViewModel并在其中添加一个TrackingViewModel.swift文件。

    import SwiftUI import AmazonLocationiOSAuthSDK import MapLibre final class TrackingViewModel : ObservableObject { @Published var trackingButtonText = NSLocalizedString("StartTrackingLabel", comment: "") @Published var trackingButtonColor = Color.blue @Published var trackingButtonIcon = "play.circle" @Published var region : String @Published var mapName : String @Published var indexName : String @Published var identityPoolId : String @Published var trackerName : String @Published var showAlert = false @Published var alertTitle = "" @Published var alertMessage = "" @Published var centerLabel = "" var clientIntialised: Bool var client: LocationTracker! var authHelper: AuthHelper var credentialsProvider: LocationCredentialsProvider? var mlnMapView: MLNMapView? var mapViewDelegate: MapViewDelegate? var lastGetTrackingTime: Date? var trackingActive: Bool init(region: String, mapName: String, indexName: String, identityPoolId: String, trackerName: String) { self.region = region self.mapName = mapName self.indexName = indexName self.identityPoolId = identityPoolId self.trackerName = trackerName self.authHelper = AuthHelper() self.trackingActive = false self.clientIntialised = false } func authWithCognito(identityPoolId: String?) { guard let identityPoolId = identityPoolId?.trimmingCharacters(in: .whitespacesAndNewlines) else { alertTitle = NSLocalizedString("Error", comment: "") alertMessage = NSLocalizedString("NotAllFieldsAreConfigured", comment: "") showAlert = true return } credentialsProvider = authHelper.authenticateWithCognitoUserPool(identityPoolId: identityPoolId) initializeClient() } func initializeClient() { client = LocationTracker(provider: credentialsProvider!, trackerName: trackerName) clientIntialised = true } }

向应用程序添加交互式地图

现在,您将向应用程序中添加地图控件。本教程使用 MapLibre 和 AWS API 来管理应用程序中的地图视图。地图控件本身是 MapLibre GL Native iOS 库的一部分。

  1. 使用以下代码在 V ie ws 文件夹下添加MapView.swift文件:

    import SwiftUI import MapLibre struct MapView: UIViewRepresentable { var onMapViewAvailable: ((MLNMapView) -> Void)? var mlnMapView: MLNMapView? var trackingViewModel: TrackingViewModel func makeCoordinator() -> MapView.Coordinator { return Coordinator(self, trackingViewModel: trackingViewModel) } func makeUIView(context: Context) -> MLNMapView { let styleURL = URL(string: "https://maps.geo.\(trackingViewModel.region).amazonaws.com/maps/v0/maps/\(trackingViewModel.mapName)/style-descriptor") let mapView = MLNMapView(frame: .zero, styleURL: styleURL) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.setZoomLevel(15, animated: true) mapView.showsUserLocation = true mapView.userTrackingMode = .follow context.coordinator.mlnMapView = mapView mapView.delegate = context.coordinator mapView.logoView.isHidden = true context.coordinator.addCenterMarker() onMapViewAvailable?(mapView) trackingViewModel.mlnMapView = mapView return mapView } func updateUIView(_ uiView: MLNMapView, context: Context) { } class Coordinator: NSObject, MLNMapViewDelegate, MapViewDelegate { var control: MapView var mlnMapView: MLNMapView? var trackingViewModel: TrackingViewModel var centerMarker: MLNPointAnnotation? public init(_ control: MapView, trackingViewModel: TrackingViewModel) { self.control = control self.trackingViewModel = trackingViewModel super.init() self.trackingViewModel.mapViewDelegate = self } func mapViewDidFinishRenderingMap(_ mapView: MLNMapView, fullyRendered: Bool) { if(fullyRendered) { mapView.accessibilityIdentifier = "MapView" mapView.isAccessibilityElement = false } } func addCenterMarker() { guard let mlnMapView = mlnMapView else { return } let centerCoordinate = mlnMapView.centerCoordinate let marker = MLNPointAnnotation() marker.coordinate = centerCoordinate marker.accessibilityLabel = "CenterMarker" mlnMapView.addAnnotation(marker) centerMarker = marker trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker) } func mapView(_ mapView: MLNMapView, regionDidChangeAnimated animated: Bool) { if let marker = centerMarker { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { mapView.deselectAnnotation(marker, animated: false) marker.coordinate = mapView.centerCoordinate let centerCoordinate = mapView.centerCoordinate self.trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker) } } } } }
  2. ViewModel文件夹下添加AWSSignatureV4Delegate文件。此文件用于对所有 MapView http 请求进行签名以渲染地图:

    import MapLibre import AmazonLocationiOSAuthSDK class AWSSignatureV4Delegate : NSObject, MLNOfflineStorageDelegate { private let awsSigner: AWSSigner init(credentialsProvider: LocationCredentialsProvider) { self.awsSigner = DENY LIST ERROR , serviceName: "geo") super.init() } func offlineStorage(_ storage: MLNOfflineStorage, urlForResourceOf kind: MLNResourceKind, with url: URL) -> URL { if url.host?.contains("amazonaws.com") != true { return url } let signedURL = awsSigner.signURL(url: url, expires: .hours(1)) return signedURL } }
  3. 在 “视图” 文件夹下添加UserLocationView.swift文件。这会添加一个按钮,该按钮可将地图居中放置在用户所在位置

    import SwiftUI struct UserLocationView: View { @ObservedObject var trackingViewModel: TrackingViewModel var body: some View { Button(action: { trackingViewModel.locateMe() }) { Image(systemName: "scope") .resizable() .frame(width: 24, height: 24) .padding(5) .background(Color.white) .foregroundColor(.blue) .clipShape(RoundedRectangle(cornerRadius: 8)) .shadow(color: Color.black.opacity(0.3), radius: 3, x: 0, y: 2) } .accessibility(identifier: "LocateMeButton") .padding(.trailing, 10) .padding(.bottom, 10) .frame(maxWidth: .infinity, alignment: .trailing) } }
  4. 使用以下代码添加TrackingView.swift文件:

    import SwiftUI struct TrackingView: View { @ObservedObject var trackingViewModel: TrackingViewModel var body: some View { ZStack(alignment: .bottom) { MapView(trackingViewModel: trackingViewModel) VStack { UserLocationView(trackingViewModel: trackingViewModel) } } .onAppear() { if !trackingViewModel.identityPoolId.isEmpty { trackingViewModel.authWithCognito(identityPoolId: trackingViewModel.identityPoolId) } } } }

您现在可以构建应用程序了。要运行它,你可能需要设置一台设备才能在 Xcode 中模拟它,或者在你的设备上使用该应用程序。使用此应用程序查看地图控件的行为方式。您可以通过在地图上拖动来进行平移,然后捏住即可缩放。您可以自行更改地图控件的工作方式,以根据应用程序的需要对其进行自定义。

现在,您将向应用程序添加反向地理编码搜索,您可以在其中找到某个位置的项目。为了简化 iOS 应用程序的使用,我们将搜索屏幕中央。要查找新位置,请将地图移动到要搜索的位置。我们将在地图的中心放置一个标记,以显示我们正在搜索的位置。

  1. 在 `TrackingViewModel.swift` 文件中添加以下与反向地理编码搜索相关的代码

    func reverseGeocodeCenter(centerCoordinate: CLLocationCoordinate2D, marker: MLNPointAnnotation) { let position = [NSNumber(value: centerCoordinate.longitude), NSNumber(value: centerCoordinate.latitude)] searchPositionAPI(position: position, marker: marker) } func searchPositionAPI(position: [Double], marker: MLNPointAnnotation) { if let amazonClient = authHelper.getLocationClient() { Task { let searchRequest = SearchPlaceIndexForPositionInput(indexName: indexName, language: "en" , maxResults: 10, position: position) let searchResponse = try? await amazonClient.searchPosition(indexName: indexName, input: searchRequest) DispatchQueue.main.async { self.centerLabel = searchResponse?.results?.first?.place?.label ?? "" self.mlnMapView?.selectAnnotation(marker, animated: true, completionHandler: {}) } } } }
  2. 使用以下代码更新TrackingView.swift文件,该代码将显示地图视图中心位置的地址

    import SwiftUI struct TrackingView: View { @ObservedObject var trackingViewModel: TrackingViewModel var body: some View { ZStack(alignment: .bottom) { if trackingViewModel.mapSigningIntialised { MapView(trackingViewModel: trackingViewModel) VStack { UserLocationView(trackingViewModel: trackingViewModel) CenterAddressView(trackingViewModel: trackingViewModel) } } else { Text("Loading...") } } .onAppear() { if !trackingViewModel.identityPoolId.isEmpty { Task { do { try await trackingViewModel.authWithCognito(identityPoolId: trackingViewModel.identityPoolId) } catch { print(error) } } } } } }

向您的应用程序添加追踪功能

应用程序的最后一步是向应用程序添加跟踪功能。在这种情况下,您将在应用程序上添加开始跟踪、停止跟踪、获取和显示跟踪点。

  1. TrackingBottomView.swift文件添加到您的项目中。它有一个用于开始和停止跟踪用户位置的按钮,并在地图上显示跟踪点。

    import SwiftUI struct TrackingBottomView: View { @ObservedObject var trackingViewModel: TrackingViewModel var body: some View { Button(action: { Task { if(trackingViewModel.trackingButtonText == NSLocalizedString("StartTrackingLabel", comment: "")) { trackingViewModel.startTracking() } else { trackingViewModel.stopTracking() } } }) { HStack { Spacer() Text("Tracking") .foregroundColor(trackingViewModel.trackingButtonColor) .background(.white) .cornerRadius(15.0) Image(systemName: trackingViewModel.trackingButtonIcon) .resizable() .frame(width: 24, height: 24) .padding(5) .background(.white) .foregroundColor(trackingViewModel.trackingButtonColor) } } .accessibility(identifier: "TrackingButton") .background(.white) .clipShape(RoundedRectangle(cornerRadius: 8)) .padding(.trailing, 10) .padding(.bottom, 40) .frame(width: 130, alignment: .trailing) .shadow(color: Color.black.opacity(0.3), radius: 3, x: 0, y: 2) } }
  2. 使用以下代码更新TrackingView.swift文件

    import SwiftUI struct TrackingView: View { @ObservedObject var trackingViewModel: TrackingViewModel var body: some View { ZStack(alignment: .bottom) { if trackingViewModel.mapSigningIntialised { MapView(trackingViewModel: trackingViewModel) VStack { UserLocationView(trackingViewModel: trackingViewModel) CenterAddressView(trackingViewModel: trackingViewModel) TrackingBottomView(trackingViewModel: trackingViewModel) } } else { Text("Loading...") } } .onAppear() { if !trackingViewModel.identityPoolId.isEmpty { Task { do { try await trackingViewModel.authWithCognito(identityPoolId: trackingViewModel.identityPoolId) } catch { print(error) } } } } } }
  3. TrackingViewModel.swift文件中添加以下代码。这些函数负责启动和停止跟踪。如果用户定位权限被拒绝,它还会显示错误警报。

  4. 要实现前台跟踪,请复制粘贴以下代码示例:

    func showLocationDeniedRationale() { alertTitle = NSLocalizedString("locationManagerAlertTitle", comment: "") alertMessage = NSLocalizedString("locationManagerAlertText", comment: "") showAlert = true } // Required in info.plist: Privacy - Location When In Use Usage Description func startTracking() { do { print("Tracking Started...") if(client == nil) { initializeClient() } try client.startTracking() DispatchQueue.main.async { [self] in self.trackingButtonText = NSLocalizedString("StopTrackingLabel", comment: "") self.trackingButtonColor = .red self.trackingButtonIcon = "pause.circle" trackingActive = true } } catch TrackingLocationError.permissionDenied { showLocationDeniedRationale() } catch { print("error in tracking") } } func stopTracking() { print("Tracking Stopped...") client.stopTracking() trackingButtonText = NSLocalizedString("StartTrackingLabel", comment: "") trackingButtonColor = .blue trackingButtonIcon = "play.circle" trackingActive = false }
    注意

    startTracking将要求用户获得定位许可。应用程序必须使用 “使用时” 或 “仅使用一次” 权限。否则,应用程序将抛出权限被拒绝的错误。

要获取和显示追踪位置,请按照以下步骤操作:

  1. 要从用户的设备上获取位置,您需要提供开始和结束日期和时间。单个调用最多返回 100 个跟踪位置,但如果追踪位置超过 100 个,它将返回 `nextToken` 值。你需要用 `nextToken getTrackerDevice `调用后续的 “Location” 调用,以便在给定的开始和结束时间加载更多的跟踪点。

    func getTrackingPoints(nextToken: String? = nil) async throws { guard trackingActive else { return } // Initialize startTime to 24 hours ago from the current date and time. let startTime: Date = Date().addingTimeInterval(-86400) var endTime: Date = Date() if lastGetTrackingTime != nil { endTime = lastGetTrackingTime! } let result = try await client?.getTrackerDeviceLocation(nextToken: nextToken, startTime: startTime, endTime: endTime) if let trackingData = result { lastGetTrackingTime = Date() let devicePositions = trackingData.devicePositions let positions = devicePositions!.sorted { (pos1: LocationClientTypes.DevicePosition, pos2: LocationClientTypes.DevicePosition) -> Bool in guard let date1 = pos1.sampleTime, let date2 = pos2.sampleTime else { return false } return date1 < date2 } let trackingPoints = positions.compactMap { position -> CLLocationCoordinate2D? in guard let latitude = position.position!.last, let longitude = position.position!.first else { return nil } return CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } DispatchQueue.main.async { self.mapViewDelegate!.drawTrackingPoints( trackingPoints: trackingPoints) } if let nextToken = trackingData.nextToken { try await getTrackingPoints(nextToken: nextToken) } } }
  2. 现在用以下代码替换MapView.swift文件中的代码:

    import SwiftUI import MapLibre struct MapView: UIViewRepresentable { var onMapViewAvailable: ((MLNMapView) -> Void)? var mlnMapView: MLNMapView? var trackingViewModel: TrackingViewModel func makeCoordinator() -> MapView.Coordinator { return Coordinator(self, trackingViewModel: trackingViewModel) } func makeUIView(context: Context) -> MLNMapView { let styleURL = URL(string: "https://maps.geo.\(trackingViewModel.region).amazonaws.com/maps/v0/maps/\(trackingViewModel.mapName)/style-descriptor") let mapView = MLNMapView(frame: .zero, styleURL: styleURL) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.setZoomLevel(15, animated: true) mapView.showsUserLocation = true mapView.userTrackingMode = .follow context.coordinator.mlnMapView = mapView mapView.delegate = context.coordinator mapView.logoView.isHidden = true context.coordinator.addCenterMarker() onMapViewAvailable?(mapView) trackingViewModel.mlnMapView = mapView return mapView } func updateUIView(_ uiView: MLNMapView, context: Context) { } class Coordinator: NSObject, MLNMapViewDelegate, MapViewDelegate { var control: MapView var mlnMapView: MLNMapView? var trackingViewModel: TrackingViewModel var centerMarker: MLNPointAnnotation? public init(_ control: MapView, trackingViewModel: TrackingViewModel) { self.control = control self.trackingViewModel = trackingViewModel super.init() self.trackingViewModel.mapViewDelegate = self } func mapViewDidFinishRenderingMap(_ mapView: MLNMapView, fullyRendered: Bool) { if(fullyRendered) { mapView.accessibilityIdentifier = "MapView" mapView.isAccessibilityElement = false } } func addCenterMarker() { guard let mlnMapView = mlnMapView else { return } let centerCoordinate = mlnMapView.centerCoordinate let marker = MLNPointAnnotation() marker.coordinate = centerCoordinate marker.accessibilityLabel = "CenterMarker" mlnMapView.addAnnotation(marker) centerMarker = marker trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker) } func mapView(_ mapView: MLNMapView, regionDidChangeAnimated animated: Bool) { if let marker = centerMarker { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { mapView.deselectAnnotation(marker, animated: false) marker.coordinate = mapView.centerCoordinate let centerCoordinate = mapView.centerCoordinate self.trackingViewModel.reverseGeocodeCenter(centerCoordinate: centerCoordinate, marker: marker) } } } func mapView(_ mapView: MLNMapView, viewFor annotation: MLNAnnotation) -> MLNAnnotationView? { guard let pointAnnotation = annotation as? MLNPointAnnotation else { return nil } let reuseIdentifier: String var color: UIColor = .black if pointAnnotation.accessibilityLabel == "Tracking" { reuseIdentifier = "TrackingAnnotation" color = UIColor(red: 0.00784313725, green: 0.50588235294, blue: 0.58039215686, alpha: 1) } else if pointAnnotation.accessibilityLabel == "LocationChange" { reuseIdentifier = "LocationChange" color = .gray } else { reuseIdentifier = "DefaultAnnotationView" } var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) if annotationView == nil { if reuseIdentifier != "DefaultAnnotationView" { annotationView = MLNAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier) //If point annotation is an uploaded Tracking point the radius is 20 and color is blue, otherwise radius is 10 and color is gray let radius = pointAnnotation.accessibilityLabel == "Tracking" ? 20:10 annotationView?.frame = CGRect(x: 0, y: 0, width: radius, height: radius) annotationView?.backgroundColor = color annotationView?.layer.cornerRadius = 10 if pointAnnotation.accessibilityLabel == "Tracking" { annotationView?.layer.borderColor = UIColor.white.cgColor annotationView?.layer.borderWidth = 2.0 annotationView?.layer.shadowColor = UIColor.black.cgColor annotationView?.layer.shadowOffset = CGSize(width: 0, height: 2) annotationView?.layer.shadowRadius = 3 annotationView?.layer.shadowOpacity = 0.2 annotationView?.clipsToBounds = false } } else { return nil } } return annotationView } func mapView(_ mapView: MLNMapView, didUpdate userLocation: MLNUserLocation?) { if (userLocation?.location) != nil { if trackingViewModel.trackingActive { let point = MLNPointAnnotation() point.coordinate = (userLocation?.location!.coordinate)! point.accessibilityLabel = "LocationChange" mapView.addAnnotation(point) Task { do { try await trackingViewModel.getTrackingPoints() } catch { print(error) } } } } } func checkIfTrackingAnnotationExists(on mapView: MLNMapView, at coordinates: CLLocationCoordinate2D) -> Bool { let existingAnnotation = mapView.annotations?.first(where: { annotation in guard let annotation = annotation as? MLNPointAnnotation else { return false } return annotation.coordinate.latitude == coordinates.latitude && annotation.coordinate.longitude == coordinates.longitude && annotation.accessibilityLabel == "Tracking" }) return existingAnnotation != nil } public func drawTrackingPoints(trackingPoints: [CLLocationCoordinate2D]?) { guard let mapView = mlnMapView, let newTrackingPoints = trackingPoints, !newTrackingPoints.isEmpty else { return } let uniqueCoordinates = newTrackingPoints.filter { coordinate in !checkIfTrackingAnnotationExists(on: mapView, at: coordinate) } let points = uniqueCoordinates.map { coordinate -> MLNPointAnnotation in let point = MLNPointAnnotation() point.coordinate = coordinate point.accessibilityLabel = "Tracking" return point } mapView.addAnnotations(points) } } } protocol MapViewDelegate: AnyObject { func drawTrackingPoints(trackingPoints: [CLLocationCoordinate2D]?) }

要本地化字符串值,请按以下步骤操作。

  1. 创建并添加一个名为的新文件Localizable.xcstrings

  2. 右键单击该Localizable.xcstrings文件并将其作为源代码打开。

  3. 将其内容替换为以下内容:

    { "sourceLanguage" : "en", "strings" : { "Cancel" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Cancel" } } } }, "Error" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Error" } } } }, "Loading..." : { }, "locationManagerAlertText" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Allow \\\"Quick Start App\\\" to use your location" } } } }, "locationManagerAlertTitle" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "We need your location to detect your location in map" } } } }, "NotAllFieldsAreConfigured" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Not all the fields are configured" } } } }, "OK" : { "extractionState" : "manual", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "OK" } } } }, "StartTrackingLabel" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Start Tracking" } } } }, "StopTrackingLabel" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", "value" : "Stop Tracking" } } } }, "Tracking" : { } }, "version" : "1.0" }
  4. 保存您的文件,然后构建并运行您的应用程序以预览功能。

  5. 允许位置权限,然后点击跟踪按钮。该应用程序将开始上传用户位置并将其上传到亚马逊位置追踪器。它还将在地图上显示用户位置的变化、追踪点和当前地址。

您的快速入门应用程序已完成。本教程向您展示了如何创建一个 iOS 应用程序,该应用程序:

  • 创建用户可以与之交互的地图。

  • 处理与用户更改地图视图相关的多个地图事件。

  • 调用 Amazon Location Service API,专门用于使用亚马逊定位的 searchByPosition API 在某个位置搜索地图。

接下来做什么

此应用程序的源代码可在上找到GitHub

要了解更多关于 Amazon Location 的信息,您可以查看以下资源: