diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec index 4971088c..b930899d 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios.podspec @@ -14,8 +14,7 @@ A new Flutter project. s.source = { :path => '.' } s.source_files = 'arcgis_map_sdk_ios/Sources/**/*' s.dependency 'Flutter' - s.dependency 'ArcGIS-Runtime-Toolkit-iOS' - s.platform = :ios, '13.0' + s.platform = :ios, '16.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift index c16447b7..dde81e36 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Package.swift @@ -6,19 +6,19 @@ import PackageDescription let package = Package( name: "arcgis_map_sdk_ios", platforms: [ - .iOS("14.0") + .iOS("16.0") ], products: [ .library(name: "arcgis-map-sdk-ios", targets: ["arcgis_map_sdk_ios"]) ], dependencies: [ - .package(url: "https://github.com/Esri/arcgis-runtime-ios", .upToNextMinor(from: "100.15.0")), + .package(url: "https://github.com/Esri/arcgis-maps-sdk-swift", .upToNextMinor(from: "200.7.0")), ], targets: [ .target( name: "arcgis_map_sdk_ios", dependencies: [ - .product(name: "ArcGIS", package: "arcgis-runtime-ios") + .product(name: "ArcGIS", package: "arcgis-maps-sdk-swift") ] ), ] diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift index a7d86fea..6088a4ba 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ArcgisMapView.swift @@ -1,10 +1,10 @@ import ArcGIS import Foundation import Flutter +import SwiftUI -class ArcgisMapView: NSObject, FlutterPlatformView { - private let defaultGraphicsOverlay = AGSGraphicsOverlay() +class ArcgisMapView: NSObject, FlutterPlatformView { private let methodChannel: FlutterMethodChannel private let zoomEventChannel: FlutterEventChannel @@ -13,29 +13,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { private let centerPositionStreamHandler = CenterPositionStreamHandler() private let flutterPluginRegistrar: FlutterPluginRegistrar - private var mapLoadStatusObservation: NSKeyValueObservation? - - private var mapScaleObservation: NSKeyValueObservation? - private var mapVisibleAreaObservation: NSKeyValueObservation? - - private let initialZoom: Int - - private var mapView: AGSMapView - private let map = AGSMap() - private let graphicsOverlay = AGSGraphicsOverlay() - private let userIndicatorGraphic = AGSGraphic() - private let pinGraphic = AGSGraphic() - private let routeLineGraphic = AGSGraphic() - - private var routeLineGraphics = [AGSGraphic]() - - private var routePoints = Array() - - - private static let defaultDuration = 0.8 - + private var mapContentView: MapContentView + + private var hostingController: UIHostingController func view() -> UIView { - return mapView + return hostingController.view } init( @@ -61,85 +43,76 @@ class ArcgisMapView: NSObject, FlutterPlatformView { centerPositionEventChannel.setStreamHandler(centerPositionStreamHandler) if let apiKey = mapOptions.apiKey { - AGSArcGISRuntimeEnvironment.apiKey = apiKey + ArcGISEnvironment.apiKey = APIKey(apiKey) } if let licenseKey = mapOptions.licenseKey { do { - try AGSArcGISRuntimeEnvironment.setLicenseKey(licenseKey) + try ArcGISEnvironment.setLicense(with: LicenseKey(licenseKey)!) } catch { print("setLicenseKey failed. \(error)") } } + + let viewpoint = Viewpoint( + latitude: mapOptions.initialCenter.latitude, + longitude: mapOptions.initialCenter.longitude, + scale: ArcgisMapView.convertZoomLevelToMapScale(Int(mapOptions.zoom)) + ) - initialZoom = Int(mapOptions.zoom) - - mapView = AGSMapView.init(frame: frame) - + mapContentView = MapContentView(viewModel: MapViewModel(viewpoint: viewpoint)) + + // Embed the SwiftUI MapView into a UIHostingController + hostingController = UIHostingController(rootView: mapContentView) + hostingController.view.frame = frame + hostingController.view.backgroundColor = .clear + super.init() if let isAttributionTextVisible = mapOptions.isAttributionTextVisible { - mapView.isAttributionTextVisible = isAttributionTextVisible + mapContentView.viewModel.attributionBarHidden = !isAttributionTextVisible } if mapOptions.basemap != nil { - map.basemap = AGSBasemap(style: parseBaseMapStyle(mapOptions.basemap!)) + mapContentView.viewModel.map.basemap = Basemap(style: parseBaseMapStyle(mapOptions.basemap!)) } else { let layers = mapOptions.vectorTilesUrls!.map { url in - AGSArcGISVectorTiledLayer(url: URL(string: url)!) + ArcGISVectorTiledLayer(url: URL(string: url)!) } - map.basemap = AGSBasemap(baseLayers: layers, referenceLayers: nil) + mapContentView.viewModel.map.basemap = Basemap(baseLayers: layers) } - map.minScale = convertZoomLevelToMapScale(mapOptions.minZoom) - map.maxScale = convertZoomLevelToMapScale(mapOptions.maxZoom) - - mapView.map = map - mapView.graphicsOverlays.add(defaultGraphicsOverlay) + mapContentView.viewModel.map.minScale = ArcgisMapView.convertZoomLevelToMapScale(mapOptions.minZoom) + mapContentView.viewModel.map.maxScale = ArcgisMapView.convertZoomLevelToMapScale(mapOptions.maxZoom) - mapScaleObservation = mapView.observe(\.mapScale) { [weak self] (map, notifier) in - DispatchQueue.main.async { - guard let self = self else { - return - } - let newZoom = self.convertScaleToZoomLevel(self.mapView.mapScale) - self.zoomStreamHandler.addZoom(zoom: newZoom) - } + mapContentView.viewModel.onScaleChanged = { [weak self] scale in + guard let self = self else { return } + guard !scale.isNaN else { return } + let newZoom = self.convertScaleToZoomLevel(scale) + self.zoomStreamHandler.addZoom(zoom: newZoom) } - mapVisibleAreaObservation = mapView.observe(\.visibleArea) { [weak self] (map, notifier) in - DispatchQueue.main.async { - guard let self = self else { - return - } - guard let center = self.mapView.visibleArea?.extent.center else { - return - } - guard let wgs84Center = AGSGeometryEngine.projectGeometry(center, to: .wgs84()) as? AGSPoint else {return} + mapContentView.viewModel.onVisibleAreaChanged = { [weak self] polygon in + guard let self = self else { return } + let center = polygon.extent.center + if let wgs84Center = GeometryEngine.project(center, into: .wgs84) as? Point { self.centerPositionStreamHandler.add(center: LatLng(latitude: wgs84Center.y, longitude: wgs84Center.x)) } } - - let viewpoint = AGSViewpoint( - latitude: mapOptions.initialCenter.latitude, - longitude: mapOptions.initialCenter.longitude, - scale: convertZoomLevelToMapScale(Int(mapOptions.zoom)) - ) - mapView.setViewpoint(viewpoint) - setMapInteractive(mapOptions.isInteractive) setupMethodChannel() - - mapLoadStatusObservation = map.observe(\.loadStatus, options: .initial) { [weak self] (map, notifier) in - DispatchQueue.main.async { - let status = map.loadStatus - self?.notifyStatus(status) - } - } + + mapContentView.viewModel.onLoadStatusChanged = { [weak self] status in + guard let self = self else { return } + DispatchQueue.main.async { + self.notifyStatus(status) + } + } } private func setupMethodChannel() { methodChannel.setMethodCallHandler({ [self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch (call.method) { + case "on_init_complete": waitForViewToInit(call, result) case "zoom_in": onZoomIn(call, result) case "zoom_out": onZoomOut(call, result) case "rotate" : onRotate(call, result) @@ -170,12 +143,19 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } }) } + + private func waitForViewToInit(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { + if mapContentView.viewModel.mapViewProxy != nil { + result(true) + } else { + mapContentView.viewModel.onViewInit = { [weak self] in + result(true) + } + } + } private func onZoomIn(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if(mapView.mapScale.isNaN) { - result(FlutterError(code: "unknown_error", message: "MapView.mapScale is NaN. Maybe the map is not completely loaded.", details: nil)) - return - } + let currentScale = mapContentView.viewModel.viewpoint.targetScale guard let args = call.arguments as? [String: Any] else { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) @@ -186,23 +166,26 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "lodFactor not provided", details: nil)) return } - - let currentZoomLevel = convertScaleToZoomLevel(mapView.mapScale) + + let currentZoomLevel = convertScaleToZoomLevel(currentScale) let totalZoomLevel = currentZoomLevel + lodFactor - if (totalZoomLevel > convertScaleToZoomLevel(map.maxScale)) { - return + if let maxScale = mapContentView.viewModel.map.maxScale { + if totalZoomLevel > convertScaleToZoomLevel(maxScale) { + result(true) + return + } } - let newScale = convertZoomLevelToMapScale(totalZoomLevel) - mapView.setViewpointScale(newScale) { _ in - result(true) + let newScale = ArcgisMapView.convertZoomLevelToMapScale(totalZoomLevel) + Task { + do { + await mapContentView.viewModel.mapViewProxy?.setViewpointScale(newScale) + result(true) + } } } private func onZoomOut(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if(mapView.mapScale.isNaN) { - result(FlutterError(code: "unknown_error", message: "MapView.mapScale is NaN. Maybe the map is not completely loaded.", details: nil)) - return - } + let currentScale = mapContentView.viewModel.viewpoint.targetScale guard let args = call.arguments as? [String: Any] else { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) @@ -214,14 +197,20 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - let currentZoomLevel = convertScaleToZoomLevel(mapView.mapScale) + let currentZoomLevel = convertScaleToZoomLevel(currentScale) let totalZoomLevel = currentZoomLevel - lodFactor - if (totalZoomLevel < convertScaleToZoomLevel(map.minScale)) { - return + if let minScale = mapContentView.viewModel.map.minScale { + if totalZoomLevel < convertScaleToZoomLevel(minScale) { + result(true) + return + } } - let newScale = convertZoomLevelToMapScale(totalZoomLevel) - mapView.setViewpointScale(newScale) { success in - result(success) + let newScale = ArcgisMapView.convertZoomLevelToMapScale(totalZoomLevel) + Task { + do { + let success = await mapContentView.viewModel.mapViewProxy?.setViewpointScale(newScale) + result(success) + } } } @@ -230,9 +219,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) return } - - mapView.setViewpointRotation(angleDouble) { success in - result(success) + Task { + do { + let success = await mapContentView.viewModel.mapViewProxy?.setViewpointRotation(angleDouble) + result(success) + } } } @@ -245,11 +236,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { do { let padding: ViewPadding = try JsonUtil.objectOfJson(args) - mapView.contentInset = UIEdgeInsets( + mapContentView.viewModel.contentInsets = EdgeInsets( top: padding.top, - left: padding.left, + leading: padding.left, bottom: padding.bottom, - right: padding.right + trailing: padding.right ) result(true) @@ -263,29 +254,28 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) return } - do { - let point: LatLng = try JsonUtil.objectOfJson(args["point"] as! Dictionary) - let zoomLevel = args["zoomLevel"] as? Int - let animationDict = args["animationOptions"] as? Dictionary - let animationOptions: AnimationOptions? = animationDict == nil ? nil : try JsonUtil.objectOfJson(animationDict!) - - let scale: Double - - if let zoomLevel = zoomLevel { - scale = convertZoomLevelToMapScale(zoomLevel) - } else { - scale = mapView.mapScale.isNaN ? convertZoomLevelToMapScale(initialZoom) : mapView.mapScale - } - - mapView.setViewpoint( - AGSViewpoint(center: point.toAGSPoint(), scale: scale), - duration: (animationOptions?.duration ?? 0) / 1000, - curve: animationOptions?.arcgisAnimationCurve() ?? .linear - ) { success in + Task { + do { + let point: LatLng = try JsonUtil.objectOfJson(args["point"] as! Dictionary) + let zoomLevel = args["zoomLevel"] as? Int + let animationDict = args["animationOptions"] as? Dictionary + let animationOptions: AnimationOptions? = animationDict == nil ? nil : try JsonUtil.objectOfJson(animationDict!) + + let scale: Double + + if let zoomLevel = zoomLevel { + scale = ArcgisMapView.convertZoomLevelToMapScale(zoomLevel) + } else { + scale = mapContentView.viewModel.viewpoint.targetScale + } + let success = await mapContentView.viewModel.mapViewProxy?.setViewpoint( + Viewpoint(center: point.toAGSPoint(), scale: scale), + duration: (animationOptions?.duration ?? 0) / 1000 + ) result(success) + } catch { + result(FlutterError(code: "error", message: "Error onMoveCamera. Provided: \(args)", details: nil)) } - } catch { - result(FlutterError(code: "error", message: "Error onMoveCamera. Provided: \(args)", details: nil)) } } @@ -294,28 +284,27 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) return } - - do { - let payload: MoveToPointsPayload = try JsonUtil.objectOfJson(args) - let polyline = AGSPolyline(points: payload.points.map { latLng in AGSPoint(x: latLng.longitude, y:latLng.latitude, spatialReference: .wgs84()) }) - - if(payload.padding != nil) { - mapView.setViewpointGeometry(polyline.extent, padding: payload.padding!) { success in + Task { + do { + let payload: MoveToPointsPayload = try JsonUtil.objectOfJson(args) + let polyline = Polyline(points: payload.points.map { latLng in Point(x: latLng.longitude, y:latLng.latitude, spatialReference: .wgs84) }) + + if(payload.padding != nil) { + let success = try await mapContentView.viewModel.mapViewProxy!.setViewpointGeometry(polyline.extent, padding: payload.padding!) result(success) - } - } else { - mapView.setViewpointGeometry(polyline.extent) { success in + } else { + let success = try await mapContentView.viewModel.mapViewProxy!.setViewpointGeometry(polyline.extent) result(success) } + } catch { + result(FlutterError(code: "error", message: "Error onMoveCameraToPoints. Provided: \(args)", details: nil)) } - } catch { - result(FlutterError(code: "error", message: "Error onMoveCameraToPoints. Provided: \(args)", details: nil)) } } private func onAddGraphic(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { let parser = GraphicsParser(registrar: flutterPluginRegistrar) - var newGraphics = [AGSGraphic]() + var newGraphics = [Graphic]() do { newGraphics.append(contentsOf: try parser.parse(dictionary: call.arguments as! Dictionary)) } catch { @@ -324,8 +313,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } - let existingIds = defaultGraphicsOverlay.graphics.compactMap { object in - let graphic = object as! AGSGraphic + let existingIds = mapContentView.viewModel.defaultGraphicsOverlay.graphics.compactMap { object in + let graphic = object as! Graphic return graphic.attributes["id"] as? String } @@ -347,7 +336,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { // them in this for loop instead. // ArcGis is the best <3. newGraphics.forEach { - defaultGraphicsOverlay.graphics.add($0) + mapContentView.viewModel.defaultGraphicsOverlay.addGraphic($0) } result(true) } @@ -358,15 +347,15 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - let newGraphics = defaultGraphicsOverlay.graphics.filter({ element in - let graphic = element as! AGSGraphic - let id = graphic.attributes["id"] as? String - return id != graphicId - }) + let selectedGraphics = mapContentView.viewModel.defaultGraphicsOverlay.graphics.filter { element in + if let graphic = element as? Graphic, + let id = graphic.attributes["id"] as? String { + return id == graphicId + } + return false + } - defaultGraphicsOverlay.graphics.removeAllObjects() - defaultGraphicsOverlay.graphics.addObjects(from: newGraphics) - + mapContentView.viewModel.defaultGraphicsOverlay.removeGraphics(selectedGraphics) result(true) } @@ -376,17 +365,20 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - map.basemap = AGSBasemap(style: parseBaseMapStyle(baseMapString)) - + mapContentView.viewModel.map.basemap = Basemap(style: parseBaseMapStyle(baseMapString)) result(true) } private func onRetryLoad(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - mapView.map!.retryLoad() - result(true) + Task { + do { + try await mapContentView.viewModel.map.retryLoad() + result(true) + } + } } - private func notifyStatus(_ status: AGSLoadStatus) { + private func notifyStatus(_ status: LoadStatus) { methodChannel.invokeMethod("onStatusChanged", arguments: status.jsonValue()) } @@ -406,16 +398,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } private func setMapInteractive(_ enabled: Bool) { - mapView.interactionOptions.isZoomEnabled = enabled - mapView.interactionOptions.isPanEnabled = enabled - mapView.interactionOptions.isFlickEnabled = enabled - mapView.interactionOptions.isRotateEnabled = enabled - mapView.interactionOptions.isEnabled = enabled - // don't set "isMagnifierEnabled" since we don't want to use this feature + mapContentView.viewModel.interactionModes = enabled ? .all : [] } - private func parseBaseMapStyle(_ string: String) -> AGSBasemapStyle { - let baseMapStyle = AGSBasemapStyle.allCases.first { enumValue in + private func parseBaseMapStyle(_ string: String) -> Basemap.Style { + let baseMapStyle = Basemap.Style.allCases.first { enumValue in enumValue.getJsonValue() == string } if baseMapStyle == nil { @@ -437,42 +424,47 @@ class ArcgisMapView: NSObject, FlutterPlatformView { * Convert zoom level to map scale * https://developers.arcgis.com/documentation/mapping-apis-and-services/reference/zoom-levels-and-scale/#conversion-tool * */ - private func convertZoomLevelToMapScale(_ zoomLevel: Int) -> Double { + private static func convertZoomLevelToMapScale(_ zoomLevel: Int) -> Double { 591657527 * (exp(-0.693 * Double(zoomLevel))) } private func onStartLocationDisplayDataSource(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - mapView.locationDisplay.dataSource.start { error in - if let error = error { + Task { + do { + try await mapContentView.viewModel.locationDisplay.dataSource.start(); + result(true) + } + catch{ let flutterError = FlutterError( code: "generic_error", message: "Failed to start data source: \(error.localizedDescription)", details: nil ) result(flutterError) - } else { - result(true) } } } private func onStopLocationDisplayDataSource(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - mapView.locationDisplay.dataSource.stop { - result(true) + Task { + do { + await mapContentView.viewModel.locationDisplay.dataSource.stop() + result(true) + } } } private func onSetLocationDisplayDefaultSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - operationWithSymbol(call, result) { mapView.locationDisplay.defaultSymbol = $0 } + operationWithSymbol(call, result) { mapContentView.viewModel.locationDisplay.defaultSymbol = $0 } } private func onSetLocationDisplayAccuracySymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - operationWithSymbol(call, result) { mapView.locationDisplay.accuracySymbol = $0 } + operationWithSymbol(call, result) { mapContentView.viewModel.locationDisplay.accuracySymbol = $0 } } private func onSetLocationDisplayPingAnimationSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - operationWithSymbol(call, result) { mapView.locationDisplay.pingAnimationSymbol = $0 } + operationWithSymbol(call, result) { mapContentView.viewModel.locationDisplay.pingAnimationSymbol = $0 } } private func onSetLocationDisplayUseCourseSymbolOnMove(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { @@ -481,13 +473,13 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.locationDisplay.useCourseSymbolOnMovement = active + mapContentView.viewModel.locationDisplay.usesCourseSymbolOnMovement = active result(true) } private func onUpdateLocationDisplaySourcePositionManually(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let dataSource = mapView.locationDisplay.dataSource - guard let source = dataSource as? ManualLocationDataSource else { + let dataSource = mapContentView.viewModel.locationDisplay.dataSource + guard let source = dataSource as? CustomLocationDataSource else { result(FlutterError(code: "invalid_state", message: "Expected ManualLocationDataSource but got \(dataSource)", details: nil)) return } @@ -497,7 +489,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - source.setNewLocation(position) + source.currentProvider?.setNewLocation(position) result(true) } @@ -512,14 +504,14 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.locationDisplay.autoPanMode = autoPanMode + mapContentView.viewModel.locationDisplay.autoPanMode = autoPanMode result(true) } private func onGetAutoPanMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { // autoPanMode.rawValue is any of [0; 3]: // https://developers.arcgis.com/ios/api-reference/_a_g_s_location_display_8h.html - guard let stringName = mapView.locationDisplay.autoPanMode.toName() else { + guard let stringName = mapContentView.viewModel.locationDisplay.autoPanMode.toName() else { result(FlutterError(code: "invalid_data", message: "AutoPanMode has invalid state", details: nil)) return } @@ -532,16 +524,16 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.locationDisplay.wanderExtentFactor = Float(factor) + mapContentView.viewModel.locationDisplay.wanderExtentFactor = Float(factor) result(true) } private func onGetWanderExtentFactor(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - return result(mapView.locationDisplay.wanderExtentFactor) + return result(mapContentView.viewModel.locationDisplay.wanderExtentFactor) } private func onSetLocationDisplayDataSourceType(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - if(mapView.locationDisplay.dataSource.status == .started) { + if(mapContentView.viewModel.locationDisplay.dataSource.status == .started) { result(FlutterError(code: "invalid_state", message: "Current data source is running. Make sure to stop it before setting a new data source", details: nil)) return } @@ -553,10 +545,12 @@ class ArcgisMapView: NSObject, FlutterPlatformView { switch(type) { case "manual" : - mapView.locationDisplay.dataSource = ManualLocationDataSource() + mapContentView.viewModel.locationDisplay.dataSource = CustomLocationDataSource{ + return CustomLocationProvider() + } result(true) case "system" : - mapView.locationDisplay.dataSource = AGSCLLocationDataSource() + mapContentView.viewModel.locationDisplay.dataSource = SystemLocationDataSource() result(true) default: result(FlutterError(code: "invalid_data", message: "Unknown data source type \(String(describing: type))", details: nil)) @@ -569,26 +563,26 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - mapView.isAttributionTextVisible = isVisible + mapContentView.viewModel.attributionBarHidden = !isVisible result(true) } private func onExportImage(_ result: @escaping FlutterResult) { - mapView.exportImage { image, error in - if let error = error { - result(FlutterError(code: "export_error", message: error.localizedDescription, details: nil)) - return - } - - if let image = image, let imageData = image.pngData() { - result(FlutterStandardTypedData(bytes: imageData)) - } else { - result(FlutterError(code: "conversion_error", message: "Failed to convert image to PNG data", details: nil)) - } - } - } - - private func operationWithSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, handler: (AGSSymbol) -> Void) { + Task { + do { + let image = try await mapContentView.viewModel.mapViewProxy!.exportImage() + if let imageData = image.pngData() { + result(FlutterStandardTypedData(bytes: imageData)) + } else { + result(FlutterError(code: "conversion_error", message: "Failed to convert image to PNG data", details: nil)) + } + } catch { + result(FlutterError(code: "export_error", message: error.localizedDescription, details: nil)) + } + } + } + + private func operationWithSymbol(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, handler: (Symbol) -> Void) { do { guard let args = call.arguments as? [String: Any] else { result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) @@ -604,8 +598,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } } -extension AGSBasemapStyle: CaseIterable { - public static var allCases: [AGSBasemapStyle] { +extension Basemap.Style: CaseIterable { + public static var allCases: [Basemap.Style] { [ .arcGISImagery, .arcGISImageryStandard, @@ -657,7 +651,7 @@ extension AGSBasemapStyle: CaseIterable { } } -extension AGSBasemapStyle { +extension Basemap.Style { func getJsonValue() -> String? { switch self { case .arcGISImagery: @@ -763,19 +757,17 @@ struct MoveToPointsPayload : Codable { let padding : Double? } -extension AGSLoadStatus { +extension LoadStatus { func jsonValue() -> String { switch self { case .loaded: return "loaded" case .loading: return "loading" - case .failedToLoad: - return "failedToLoad" + case .failed: + return "failed" case .notLoaded: return "notLoaded" - case .unknown: - return "unknown" @unknown default: return "unknown" } @@ -783,7 +775,7 @@ extension AGSLoadStatus { } extension String { - func autoPanModeFromString() -> AGSLocationDisplayAutoPanMode? { + func autoPanModeFromString() -> LocationDisplay.AutoPanMode? { switch self { case "compassNavigation": return .compassNavigation @@ -799,7 +791,7 @@ extension String { } } -extension AGSLocationDisplayAutoPanMode { +extension LocationDisplay.AutoPanMode { func toName() -> String? { switch self { case .off: diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift new file mode 100644 index 00000000..586f10db --- /dev/null +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/CustomLocationProvider.swift @@ -0,0 +1,48 @@ +// +// Created by Julian Bissekkou on 27.11.23. +// + +import Foundation +import ArcGIS + +final class CustomLocationProvider: LocationProvider { + private var locationContinuation: AsyncStream.Continuation? + private var headingContinuation: AsyncStream.Continuation? + + + // Exposed stream + var locations: AsyncStream { + AsyncStream { @Sendable continuation in + self.locationContinuation = continuation + continuation.onTermination = { _ in + self.locationContinuation = nil + } + } + } + + var headings: AsyncStream { + AsyncStream { continuation in + self.headingContinuation = continuation + continuation.onTermination = { @Sendable _ in + self.headingContinuation = nil + } + } + } + + // Push location from outside + public func setNewLocation(_ position: UserPosition) { + let loc = Location( + position: position.latLng.toAGSPoint(), + horizontalAccuracy: position.accuracy ?? 0, + verticalAccuracy: position.accuracy ?? 0, + speed: position.velocity ?? 0, + course: position.heading ?? 0, + isLastKnown: false + ) + locationContinuation?.yield(loc) + if let heading = position.heading { + headingContinuation?.yield(heading) + } + } +} + diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift index b2da6948..7ed30b1e 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/GraphicsParser.swift @@ -16,10 +16,10 @@ class GraphicsParser { self.registrar = registrar } - func parse(dictionary: Dictionary) throws -> [AGSGraphic] { + func parse(dictionary: Dictionary) throws -> [Graphic] { let type = dictionary["type"] as! String - let newGraphics: [AGSGraphic] + let newGraphics: [Graphic] switch (type) { case "point": newGraphics = try! parsePoint(dictionary) @@ -31,26 +31,29 @@ class GraphicsParser { throw ParseException(message: "Unknown graphic type: \(type)") } - let attributes = dictionary["attributes"] as? Dictionary - if let attributes = attributes { - newGraphics.forEach { - $0.attributes.addEntries(from: attributes) + // Apply attributes to each graphic, if present + if let attributes = dictionary["attributes"] as? [String: Any] { + newGraphics.forEach { graphic in + for (key, value) in attributes { + graphic.setAttributeValue(value, forKey: key) + } } } + return newGraphics } - private func parsePoint(_ dictionary: [String: Any]) throws -> [AGSGraphic] { + private func parsePoint(_ dictionary: [String: Any]) throws -> [Graphic] { let point: LatLng = try! JsonUtil.objectOfJson(dictionary["point"] as! Dictionary) - let graphic = AGSGraphic() + let graphic = Graphic() graphic.geometry = point.toAGSPoint() graphic.symbol = try! parseSymbol(dictionary["symbol"] as! Dictionary) return [graphic] } - private func parsePolyline(_ dictionary: [String: Any]) throws -> [AGSGraphic] { + private func parsePolyline(_ dictionary: [String: Any]) throws -> [Graphic] { let payload: PathPayload = try! JsonUtil.objectOfJson(dictionary) return try payload.paths.map { coordinates in @@ -59,28 +62,28 @@ class GraphicsParser { throw ParseException(message: "Size of coordinates need to be at least 2. Got \(array)") } if(array.count > 2) { - return AGSPoint(x: array[0], y: array[1], z: array[2], spatialReference: .wgs84()) + return Point(x: array[0], y: array[1], z: array[2], spatialReference: .wgs84) } - return AGSPoint(x: array[0], y: array[1], spatialReference: .wgs84()) + return Point(x: array[0], y: array[1], spatialReference: .wgs84) } - let graphic = AGSGraphic() - graphic.geometry = AGSPolyline(points: points) + let graphic = Graphic() + graphic.geometry = Polyline(points: points) graphic.symbol = try! parseSymbol(dictionary["symbol"] as! Dictionary) return graphic } } - private func parsePolygon(_ dictionary: [String: Any]) throws -> [AGSGraphic] { + private func parsePolygon(_ dictionary: [String: Any]) throws -> [Graphic] { let payload: PolygonPayload = try! JsonUtil.objectOfJson(dictionary) return payload.rings.map { ring in - let graphic = AGSGraphic() + let graphic = Graphic() let points = ring.map { coordinate in - AGSPoint(x: coordinate[0], y: coordinate[1], spatialReference: .wgs84()) + Point(x: coordinate[0], y: coordinate[1], spatialReference: .wgs84) } - graphic.geometry = AGSPolygon(points: points) + graphic.geometry = Polygon(points: points) graphic.symbol = try! parseSymbol(dictionary["symbol"] as! Dictionary) return graphic @@ -89,7 +92,7 @@ class GraphicsParser { // region symbol parsing - func parseSymbol(_ dictionary: [String: Any]) throws -> AGSSymbol { + func parseSymbol(_ dictionary: [String: Any]) throws -> Symbol { let type = dictionary["type"] as! String; switch (type) { case "simple-marker": @@ -105,13 +108,13 @@ class GraphicsParser { } } - private func parseSimpleMarkerSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parseSimpleMarkerSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: SimpleMarkerSymbolPayload = try! JsonUtil.objectOfJson(dictionary) - let symbol = AGSSimpleMarkerSymbol() + let symbol = SimpleMarkerSymbol() symbol.color = payload.color.toUIColor() symbol.size = payload.size - symbol.outline = AGSSimpleLineSymbol( + symbol.outline = SimpleLineSymbol( style: .solid, color: payload.outlineColor.toUIColor(), width: payload.outlineWidth @@ -119,13 +122,13 @@ class GraphicsParser { return symbol } - private func parseSimpleFillMarkerSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parseSimpleFillMarkerSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: SimpleFillSymbolPayload = try! JsonUtil.objectOfJson(dictionary) - let symbol = AGSSimpleFillSymbol() + let symbol = SimpleFillSymbol() symbol.color = payload.fillColor.toUIColor() - let outline = AGSSimpleLineSymbol() + let outline = SimpleLineSymbol() outline.width = payload.outlineWidth outline.color = payload.outlineColor.toUIColor() symbol.outline = outline @@ -133,12 +136,12 @@ class GraphicsParser { return symbol } - private func parsePictureMarkerSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parsePictureMarkerSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: PictureMarkerSymbolPayload = try! JsonUtil.objectOfJson(dictionary) if(!payload.assetUri.isWebUrl()) { let uiImage = getFlutterUiImage(payload.assetUri) - let symbol = AGSPictureMarkerSymbol(image: uiImage!) + let symbol = PictureMarkerSymbol(image: uiImage!) symbol.width = payload.width symbol.height = payload.height symbol.offsetX = payload.xOffset @@ -146,7 +149,7 @@ class GraphicsParser { return symbol } - let symbol = AGSPictureMarkerSymbol(url: URL(string: payload.assetUri)!) + let symbol = PictureMarkerSymbol(url: URL(string: payload.assetUri)!) symbol.width = payload.width symbol.height = payload.height symbol.offsetX = payload.xOffset @@ -161,9 +164,9 @@ class GraphicsParser { return UIImage(named: path!) } - private func parseSimpleLineSymbol(_ dictionary: [String: Any]) -> AGSSymbol { + private func parseSimpleLineSymbol(_ dictionary: [String: Any]) -> Symbol { let payload: SimpleLineSymbolPayload = try! JsonUtil.objectOfJson(dictionary) - let symbol = AGSSimpleLineSymbol() + let symbol = SimpleLineSymbol() if let color = payload.color { symbol.color = color.toUIColor() diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift deleted file mode 100644 index 0d266a53..00000000 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/ManualLocationDataSource.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// ManualLocationDataSource.swift -// arcgis_map_sdk_ios -// -// Created by Julian Bissekkou on 27.11.23. -// - -import Foundation -import ArcGIS - -class ManualLocationDataSource: AGSLocationDataSource { - public func setNewLocation(_ position: UserPosition) { - let loc = AGSLocation( - position: position.latLng.toAGSPoint(), - horizontalAccuracy: position.accuracy ?? 0, - velocity: position.velocity ?? 0, - course: position.heading ?? 0, - lastKnown: false - ) - didUpdate(loc) - } -} diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift new file mode 100644 index 00000000..ba06ae40 --- /dev/null +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/MapContentView.swift @@ -0,0 +1,84 @@ +// +// Created by Tarek Tolba on 29/04/2025. +// + +import SwiftUI +import ArcGIS +import CoreLocation + + +struct MapContentView: View { + @ObservedObject var viewModel: MapViewModel + + init(viewModel: MapViewModel) { + self.viewModel = viewModel + } + + var body: some View { + MapViewReader { mapViewProxy in + MapView( + map: viewModel.map, + viewpoint: viewModel.viewpoint, + graphicsOverlays: [viewModel.defaultGraphicsOverlay] + ) + .attributionBarHidden(viewModel.attributionBarHidden) + .locationDisplay(viewModel.locationDisplay) + .contentInsets(viewModel.contentInsets) + .interactionModes(viewModel.interactionModes) + .onViewpointChanged(kind: .centerAndScale) { newViewpoint in + viewModel.viewpoint = newViewpoint + } + .onScaleChanged { scale in + viewModel.onScaleChanged?(scale) + } + .onVisibleAreaChanged { polygon in + viewModel.onVisibleAreaChanged?(polygon) + } + .onChange(of: viewModel.map.basemap?.loadStatus) { newValue in + if let newValue { + viewModel.onLoadStatusChanged?(newValue) + } + } + .task { + // Store the mapViewProxy for external access + viewModel.mapViewProxy = mapViewProxy + viewModel.onViewInit?(); + } + .onDisappear { + viewModel.stopLocationDataSource() + // Clear the mapViewProxy reference when view disappears + viewModel.mapViewProxy = nil + } + .ignoresSafeArea(edges: .all) + } + } +} + + +class MapViewModel: ObservableObject { + let map = Map() + let locationDisplay = LocationDisplay() + + @Published var viewpoint: Viewpoint + @Published var mapViewProxy: MapViewProxy? + @Published var attributionBarHidden: Bool = false + @Published var contentInsets: EdgeInsets = EdgeInsets() + @Published var interactionModes: MapViewInteractionModes = .all + @Published var defaultGraphicsOverlay = GraphicsOverlay() + + var onScaleChanged: ((Double) -> Void)? + var onVisibleAreaChanged: ((Polygon) -> Void)? + var onLoadStatusChanged: ((LoadStatus) -> Void)? + var onViewInit: (() -> Void)? + + init(viewpoint : Viewpoint) { + self.viewpoint = viewpoint + } + + /// Stops the location data source. + func stopLocationDataSource() { + Task { + await locationDisplay.dataSource.stop() + } + } +} diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift index f4e65e3b..d6434e70 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/AnimationOptions.swift @@ -13,19 +13,3 @@ struct AnimationOptions: Codable { var animationCurve: String } -extension AnimationOptions { - func arcgisAnimationCurve() -> AGSAnimationCurve { - switch animationCurve { - case "linear": - return .linear - case "easeIn": - return .easeInCirc - case "easeOut": - return .easeOutCirc - case "easeInOut": - return .easeInOutCirc - default: - return .linear - } - } -} diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift index 3d7a1432..2922ac96 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/LatLng.swift @@ -15,7 +15,7 @@ struct LatLng: Codable { } extension LatLng { - func toAGSPoint() -> AGSPoint { - AGSPoint(x: longitude, y: latitude, spatialReference: .wgs84()) + func toAGSPoint() -> Point { + Point(x: longitude, y: latitude, spatialReference: .wgs84) } } diff --git a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift index fd159b27..08f2d2ce 100644 --- a/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift +++ b/arcgis_map_sdk_ios/ios/arcgis_map_sdk_ios/Sources/arcgis_map_sdk_ios/Models/Symbols/SimpleLineSymbolPayload.swift @@ -33,7 +33,7 @@ enum PolylineStyle: String, Codable { case shortDot case solid - func toAGSStyle() -> AGSSimpleLineSymbolStyle { + func toAGSStyle() -> SimpleLineSymbol.Style { switch (self) { case .dash: return .dash @@ -48,7 +48,7 @@ enum PolylineStyle: String, Codable { case .longDashDotDot: return .longDashDot case .none: - return .null + return .noLine case .shortDash: return .shortDash case .shortDashDot: @@ -68,7 +68,7 @@ enum MarkerPlacement: String, Codable { case end case beginEnd - func toAGSStyle() -> AGSSimpleLineSymbolMarkerPlacement { + func toAGSStyle() -> SimpleLineSymbol.MarkerPlacement { switch (self) { case .begin: return .begin @@ -88,12 +88,12 @@ enum MarkerStyle: String, Codable { self = try MarkerStyle(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .none } - func toAGSStyle() -> AGSSimpleLineSymbolMarkerStyle { + func toAGSStyle() -> SimpleLineSymbol.MarkerStyle { switch (self) { case .arrow: return .arrow case .none: - return .none + return .noMarkers } } } diff --git a/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart b/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart index 3b406102..8b023135 100644 --- a/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart +++ b/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:arcgis_map_sdk_method_channel/src/model_extension.dart'; import 'package:arcgis_map_sdk_platform_interface/arcgis_map_sdk_platform_interface.dart'; import 'package:flutter/services.dart'; @@ -14,7 +16,11 @@ class MethodChannelArcgisMapPlugin extends ArcgisMapPlatform { /// This method is called when the plugin is first initialized. @override - Future init(int mapId) async {} + Future init(int mapId) async { + if (Platform.isIOS) { + return _methodChannelBuilder(mapId).invokeMethod('on_init_complete'); + } + } @override Future addFeatureLayer( diff --git a/example/ios/Podfile b/example/ios/Podfile index 10f3c9b4..dfab90da 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '13.0' +platform :ios, '16.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e9d4ff56..db90865b 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -17,6 +17,6 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 geolocator_apple: d981750b9f47dbdb02427e1476d9a04397beb8d9 -PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b +PODFILE CHECKSUM: ae273562e2241d2b1bd4b6d2d349a48b8fe1a2a7 COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 203a2552..582383e8 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -179,7 +179,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; @@ -369,7 +369,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -499,7 +499,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -523,7 +523,7 @@ DEVELOPMENT_TEAM = 64Z45JP26B; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -562,7 +562,7 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; diff --git a/example/lib/location_indicator_example_page.dart b/example/lib/location_indicator_example_page.dart index deca0820..6609f8a0 100644 --- a/example/lib/location_indicator_example_page.dart +++ b/example/lib/location_indicator_example_page.dart @@ -135,7 +135,6 @@ class _LocationIndicatorExamplePageState await Geolocator.requestPermission(); final location = await Geolocator.getLastKnownPosition(); if (!mounted || location == null) return; - await _controller!.moveCamera( point: LatLng(location.latitude, location.longitude), zoomLevel: 16, @@ -166,6 +165,7 @@ class _LocationIndicatorExamplePageState Future _switchLocationSource() async { await _controller!.locationDisplay.stopSource(); + _isStarted = false; await _controller!.setLocationDisplay( _isManualLocationSource ? ArcgisLocationDisplay()