diff --git a/README.md b/README.md index 1db109487..495889257 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,19 @@ The ```main``` branch of this repository contains samples configured for the lat ## Features -* Maps - Open, create, interact with and save maps -* Layers - Display vector and raster data in maps and scenes -* Features - Work with feature layers and geodatabases -* Display Information - Show graphics, popups, callouts, and sketches -* Search - Find addresses, places, and points of interest -* Edit Data - Add, delete, and edit features and attachments -* Geometry - Create geometries and perform geometric operations -* Route & Directions - Calculate routes between locations and around barriers -* Analysis - Perform spatial analysis via geoprocessing tasks and services -* Cloud & Portal - Search for webmaps and list portal group users -* Scenes - Visualize 3D environments and symbols -* Utility Network - Work with utility networks, performing traces and exploring network elements -* Augmented Reality - View data overlaid on the real world through your device's camera +* [Maps](arcgis-ios-sdk-samples/Maps) - Open, create, interact with and save maps +* [Layers](arcgis-ios-sdk-samples/Layers) - Display vector and raster data in maps and scenes +* [Features](arcgis-ios-sdk-samples/Features) - Work with feature layers and geodatabases +* [Display Information](arcgis-ios-sdk-samples/Display%20information) - Show graphics, popups, callouts, and sketches +* [Search](arcgis-ios-sdk-samples/Search) - Find addresses, places, and points of interest +* [Edit Data](arcgis-ios-sdk-samples/Edit%20data) - Add, delete, and edit features and attachments +* [Geometry](arcgis-ios-sdk-samples/Geometry) - Create geometries and perform geometric operations +* [Route & Directions](arcgis-ios-sdk-samples/Route%20and%20directions) - Calculate routes between locations and around barriers +* [Analysis](arcgis-ios-sdk-samples/Analysis) - Perform spatial analysis via geoprocessing tasks and services +* [Cloud & Portal](arcgis-ios-sdk-samples/Cloud%20and%20portal) - Search for webmaps and list portal group users +* [Scenes](arcgis-ios-sdk-samples/Scenes) - Visualize 3D environments and symbols +* [Utility Network](arcgis-ios-sdk-samples/Utility%20network) - Work with utility networks, performing traces and exploring network elements +* [Augmented Reality](arcgis-ios-sdk-samples/Augmented%20reality) - View data overlaid on the real world through your device's camera ## Requirements @@ -45,7 +45,18 @@ The *ArcGIS Runtime SDK Samples app* has a *Target SDK* version of *13.0*, meani 1. **Fork** and then **clone** the repository 1. **Install** the ArcGIS Runtime SDK for iOS by running the `pod install` command in the folder where you cloned this repository 1. **Open** the `arcgis-ios-sdk-samples.xcworkspace` **workspace** file -1. **Select** the `arcgis-ios-sdk-samples` project node, go to the `Swift Packages` tab, and **delete** the `arcgis-runtime-toolkit-ios` package. This Swift package conflicts with CocoaPods and is only required when using the Swift Package Manager as described in the previous section. +1. **Select** the `arcgis-ios-sdk-samples` project node, go to the `Package Dependencies` tab, and **delete** the `arcgis-runtime-toolkit-ios` package + > This Swift package conflicts with CocoaPods and is only required when using the Swift Package Manager as described in the previous section. +1. **Run** the `arcgis-ios-sdk-samples` app target + +## Building Samples Using Installed SDK + +1. **Fork** and then **clone** the repository +1. **Install** the ArcGIS Runtime SDK for iOS to a central location on your mac as described [here](https://developers.arcgis.com/ios/install-and-set-up/#manual-download) +1. **Open** the `arcgis-ios-sdk-samples.xcodeproj` **project** file. **Select** the `arcgis-ios-sdk-samples` project node, go to the `Package Dependencies` tab, and **delete** the `arcgis-runtime-toolkit-ios` package + > This Swift package conflicts with manual installation and is only required when using the Swift Package Manager as described in the previous section. +1. **Download** the `arcgis-runtime-toolkit-ios` from [here](https://github.com/Esri/arcgis-runtime-toolkit-ios), and follow the [instructions](https://github.com/Esri/arcgis-runtime-toolkit-ios#manual) to add it as a local Swift package + > The manual installation method allows you to use a local installation ArcGIS Runtime SDK for iOS by making minor edits to the toolkit's Swift package manifest. Follow the instructions in `Package.swift` to uncomment the lines for `localArcGISPackage` and use it as the package dependency instead of the hosted version. 1. **Run** the `arcgis-ios-sdk-samples` app target ## Sample Data diff --git a/Scripts/CI/README_Metadata_StyleCheck/README_style_checker.py b/Scripts/CI/README_Metadata_StyleCheck/README_style_checker.py index 0e8b040c3..f382dfcde 100644 --- a/Scripts/CI/README_Metadata_StyleCheck/README_style_checker.py +++ b/Scripts/CI/README_Metadata_StyleCheck/README_style_checker.py @@ -33,7 +33,8 @@ 'Network Link Control', 'Open Street Map', 'OpenStreetMap', - 'Play a KML Tour' + 'Play a KML Tour', + 'SwiftUI' } # A set of category folder names in current sample viewer. diff --git a/arcgis-ios-sdk-samples.xcodeproj/project.pbxproj b/arcgis-ios-sdk-samples.xcodeproj/project.pbxproj index 970dd3753..edb6b320d 100644 --- a/arcgis-ios-sdk-samples.xcodeproj/project.pbxproj +++ b/arcgis-ios-sdk-samples.xcodeproj/project.pbxproj @@ -49,6 +49,9 @@ 003D25702513FBB5007527C2 /* ExchangeSetWithoutUpdates in Resources */ = {isa = PBXBuildFile; fileRef = 003D256F2513FBB5007527C2 /* ExchangeSetWithoutUpdates */; settings = {ASSET_TAGS = (ExchangeSetWithoutUpdates, ); }; }; 003D25712513FC61007527C2 /* AddENCExchangeSetViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 003D256B2513F4A6007527C2 /* AddENCExchangeSetViewController.swift */; }; 005507FC25E48134003990D3 /* Redlands.nmea in Resources */ = {isa = PBXBuildFile; fileRef = 005507FB25E48134003990D3 /* Redlands.nmea */; settings = {ASSET_TAGS = (RedlandsNMEA, ); }; }; + 005C713A26C32ED10003BDC3 /* DisplayMapSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005C713926C32ED10003BDC3 /* DisplayMapSwiftUIView.swift */; }; + 005C713C26C331470003BDC3 /* DisplayMapSwiftUI.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 005C713B26C331470003BDC3 /* DisplayMapSwiftUI.storyboard */; }; + 005C713E26C331B90003BDC3 /* DisplayMapSwiftUIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005C713D26C331B90003BDC3 /* DisplayMapSwiftUIViewController.swift */; }; 00686FD02480889900EDA705 /* NavigateARNavigatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00686FCF2480889900EDA705 /* NavigateARNavigatorViewController.swift */; }; 00686FD52481CF4D00EDA705 /* NavigateARRoutePlannerViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00C024E42475E4AB00E1DA8D /* NavigateARRoutePlannerViewController.swift */; }; 00686FD62481CF4D00EDA705 /* NavigateARNavigatorViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00686FCF2480889900EDA705 /* NavigateARNavigatorViewController.swift */; }; @@ -64,11 +67,15 @@ 007FC9492534E8AE00D8D9A1 /* solarized-dark.css in Resources */ = {isa = PBXBuildFile; fileRef = 007FC9472534E8AE00D8D9A1 /* solarized-dark.css */; }; 0083AA192628ED18003A28C8 /* ProgressViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0083AA162628ED18003A28C8 /* ProgressViewController.xib */; }; 0083AA1A2628ED18003A28C8 /* ProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0083AA172628ED18003A28C8 /* ProgressViewController.swift */; }; + 00874F0126DD8C2A009148CF /* SwiftUIMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00874F0026DD8C2A009148CF /* SwiftUIMapView.swift */; }; 0087A55D265C8FEA00D9601D /* BrowseOGCAPIFeatureServiceViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 003267B5265C479200DB4CB5 /* BrowseOGCAPIFeatureServiceViewController.swift */; }; 008D175224EEEEBD0001BB8F /* loudoun_anno.geodatabase in Resources */ = {isa = PBXBuildFile; fileRef = 008D175124EEEEBD0001BB8F /* loudoun_anno.geodatabase */; settings = {ASSET_TAGS = (loudoun_anno, ); }; }; 008D175724EEF3FE0001BB8F /* EditWithBranchVersioningViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008D175624EEF3FE0001BB8F /* EditWithBranchVersioningViewController.swift */; }; 008D175924EEF4390001BB8F /* EditWithBranchVersioning.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 008D175824EEF4390001BB8F /* EditWithBranchVersioning.storyboard */; }; 008D175A24EF35800001BB8F /* EditWithBranchVersioningViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008D175624EEF3FE0001BB8F /* EditWithBranchVersioningViewController.swift */; }; + 008F292A26E0424C00A5BCD3 /* DisplayMapSwiftUIViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 005C713D26C331B90003BDC3 /* DisplayMapSwiftUIViewController.swift */; }; + 008F292B26E0424C00A5BCD3 /* DisplayMapSwiftUIView.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 005C713926C32ED10003BDC3 /* DisplayMapSwiftUIView.swift */; }; + 008F292C26E0424C00A5BCD3 /* SwiftUIMapView.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00874F0026DD8C2A009148CF /* SwiftUIMapView.swift */; }; 009B5FF825E58E0000BB9A77 /* CreateSymbolStylesFromWebStylesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B5FF625E58E0000BB9A77 /* CreateSymbolStylesFromWebStylesViewController.swift */; }; 009B5FFD25E58E5A00BB9A77 /* CreateSymbolStylesFromWebStyles.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 009B5FFC25E58E5A00BB9A77 /* CreateSymbolStylesFromWebStyles.storyboard */; }; 009B600325E5DB4700BB9A77 /* CreateSymbolStylesFromWebStylesViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 009B5FF625E58E0000BB9A77 /* CreateSymbolStylesFromWebStylesViewController.swift */; }; @@ -86,6 +93,9 @@ 00C024E52475E4AB00E1DA8D /* NavigateARRoutePlannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C024E42475E4AB00E1DA8D /* NavigateARRoutePlannerViewController.swift */; }; 00C024E72475E54A00E1DA8D /* NavigateAR.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00C024E62475E54A00E1DA8D /* NavigateAR.storyboard */; }; 00C94676244509B8000B6C6D /* DisplayLocationSettingsViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7237D0D2190F07500A72C76 /* DisplayLocationSettingsViewController.swift */; }; + 00CDA5C126F1289B00DEE7F4 /* SelectENCFeatures.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00CDA5BE26F1289B00DEE7F4 /* SelectENCFeatures.storyboard */; }; + 00CDA5C226F1289B00DEE7F4 /* SelectENCFeaturesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CDA5BF26F1289B00DEE7F4 /* SelectENCFeaturesViewController.swift */; }; + 00CDA5C326F14B9B00DEE7F4 /* SelectENCFeaturesViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00CDA5BF26F1289B00DEE7F4 /* SelectENCFeaturesViewController.swift */; }; 00D4E8DE241AA18E002689DB /* ConvexHull.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00D4E8D9241AA18E002689DB /* ConvexHull.storyboard */; }; 00D4E8E0241AA18E002689DB /* ConvexHullViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D4E8DB241AA18E002689DB /* ConvexHullViewController.swift */; }; 00DAE5B82421321300D51281 /* NearestVertex.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00DAE5B22421321300D51281 /* NearestVertex.storyboard */; }; @@ -456,12 +466,6 @@ 3ED028EA1B8E3AA500ACA70D /* FLQueryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED028C81B8E3AA500ACA70D /* FLQueryViewController.swift */; }; 3ED028EE1B8E3AA500ACA70D /* FeatureLayerSelection.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3ED028CD1B8E3AA500ACA70D /* FeatureLayerSelection.storyboard */; }; 3ED028EF1B8E3AA500ACA70D /* FeatureLayerSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED028CE1B8E3AA500ACA70D /* FeatureLayerSelectionViewController.swift */; }; - 3ED028F41B8E3AA500ACA70D /* OnInteractionCache.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3ED028D41B8E3AA500ACA70D /* OnInteractionCache.storyboard */; }; - 3ED028F51B8E3AA500ACA70D /* OnInteractionCacheViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED028D51B8E3AA500ACA70D /* OnInteractionCacheViewController.swift */; }; - 3ED028F71B8E3AA500ACA70D /* ManualCache.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3ED028D81B8E3AA500ACA70D /* ManualCache.storyboard */; }; - 3ED028F81B8E3AA500ACA70D /* ManualCacheViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED028D91B8E3AA500ACA70D /* ManualCacheViewController.swift */; }; - 3ED028FA1B8E3AA500ACA70D /* OnInteractionNoCache.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3ED028DC1B8E3AA500ACA70D /* OnInteractionNoCache.storyboard */; }; - 3ED028FB1B8E3AA500ACA70D /* OnInteractionNoCacheViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED028DD1B8E3AA500ACA70D /* OnInteractionNoCacheViewController.swift */; }; 3ED029281B8E3AB300ACA70D /* AddGraphicsWithRenderer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3ED028FE1B8E3AB300ACA70D /* AddGraphicsWithRenderer.storyboard */; }; 3ED029291B8E3AB300ACA70D /* AddGraphicsWithRendererViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED028FF1B8E3AB300ACA70D /* AddGraphicsWithRendererViewController.swift */; }; 3ED0292C1B8E3AB300ACA70D /* GOIdentify.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3ED029031B8E3AB300ACA70D /* GOIdentify.storyboard */; }; @@ -501,9 +505,6 @@ 3ED0298C1B8E571700ACA70D /* MILUsingURLViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028A21B8E3A9800ACA70D /* MILUsingURLViewController.swift */; }; 3ED0298D1B8E571700ACA70D /* SublayersTableViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028AB1B8E3A9800ACA70D /* SublayersTableViewController.swift */; }; 3ED0298E1B8E571700ACA70D /* SublayerVisibilityViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028AD1B8E3A9800ACA70D /* SublayerVisibilityViewController.swift */; }; - 3ED0298F1B8E571700ACA70D /* OnInteractionCacheViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028D51B8E3AA500ACA70D /* OnInteractionCacheViewController.swift */; }; - 3ED029901B8E571700ACA70D /* OnInteractionNoCacheViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028DD1B8E3AA500ACA70D /* OnInteractionNoCacheViewController.swift */; }; - 3ED029911B8E571700ACA70D /* ManualCacheViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028D91B8E3AA500ACA70D /* ManualCacheViewController.swift */; }; 3ED029921B8E571700ACA70D /* FeatureLayerSelectionViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028CE1B8E3AA500ACA70D /* FeatureLayerSelectionViewController.swift */; }; 3ED029931B8E571700ACA70D /* FLQueryViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028C81B8E3AA500ACA70D /* FLQueryViewController.swift */; }; 3ED029941B8E571700ACA70D /* DefinitionExpressionViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3ED028C21B8E3AA500ACA70D /* DefinitionExpressionViewController.swift */; }; @@ -822,6 +823,9 @@ E4B2E26C1FE3321500E610FF /* ViewshedGeoElementViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = E4B2E2681FE31ED500E610FF /* ViewshedGeoElementViewController.swift */; }; E4C4A4481EE9DD790073B6C0 /* DistanceCompositeSymbol.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E4C4A4471EE9DD790073B6C0 /* DistanceCompositeSymbol.storyboard */; }; E4C4A44B1EE9DDDD0073B6C0 /* DistanceCompositeSymbolViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C4A44A1EE9DDDD0073B6C0 /* DistanceCompositeSymbolViewController.swift */; }; + F1027F4E26F91FAA000FA674 /* ExportVectorTilesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1027F4D26F91FAA000FA674 /* ExportVectorTilesViewController.swift */; }; + F1027F5426F92065000FA674 /* ExportVectorTiles.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1027F5326F92065000FA674 /* ExportVectorTiles.storyboard */; }; + F1027F5926F9258E000FA674 /* ExportVectorTilesViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = F1027F4D26F91FAA000FA674 /* ExportVectorTilesViewController.swift */; }; F1034F9C22D3EE8900B021DA /* ProjectStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1034F9922D3EE8900B021DA /* ProjectStackView.swift */; }; F1034F9D22D3EE8900B021DA /* Project.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1034F9A22D3EE8900B021DA /* Project.storyboard */; }; F1034F9E22D3EE8900B021DA /* ProjectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1034F9B22D3EE8900B021DA /* ProjectViewController.swift */; }; @@ -873,6 +877,10 @@ F1987D0323F7677E00DF41CC /* DisplaySubtypeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1987D0223F7677E00DF41CC /* DisplaySubtypeSettingsViewController.swift */; }; F1A355272411B35E000BA240 /* CreateAndSaveKMLViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = F1D1E2C22409AB9F006B2801 /* CreateAndSaveKMLViewController.swift */; }; F1BCA98F268102EB0027F5A8 /* naperville_imagery.tpkx in Resources */ = {isa = PBXBuildFile; fileRef = F1BCA98E268102EB0027F5A8 /* naperville_imagery.tpkx */; settings = {ASSET_TAGS = (naperville_imagery, ); }; }; + F1C092D4272758830000B132 /* VectorTilePackageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C092D3272758830000B132 /* VectorTilePackageViewController.swift */; }; + F1C092D7272774530000B132 /* VectorTilePackageViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = F1C092D3272758830000B132 /* VectorTilePackageViewController.swift */; }; + F1C4529F26B37008002FDB34 /* ToggleFeatureRequestModes.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1C4529E26B37008002FDB34 /* ToggleFeatureRequestModes.storyboard */; }; + F1C452A326B374C4002FDB34 /* ToggleFeatureRequestModesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C452A226B374C4002FDB34 /* ToggleFeatureRequestModesViewController.swift */; }; F1C81C4522FCEC1C00229CAA /* GetElevationPoint.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1C81C4422FCEC1C00229CAA /* GetElevationPoint.storyboard */; }; F1C81C4722FCEC3C00229CAA /* GetElevationPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C81C4622FCEC3C00229CAA /* GetElevationPointViewController.swift */; }; F1C81C4822FCED5400229CAA /* GetElevationPointViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = F1C81C4622FCEC3C00229CAA /* GetElevationPointViewController.swift */; }; @@ -882,6 +890,7 @@ F1D5022E245246EC00583CFB /* DisplayUtilityAssociationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D5022D245246EC00583CFB /* DisplayUtilityAssociationsViewController.swift */; }; F1D5022F24525E2E00583CFB /* DisplayUtilityAssociationsViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = F1D5022D245246EC00583CFB /* DisplayUtilityAssociationsViewController.swift */; }; F1D7508F230730360018B812 /* ElevationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D7508E230730360018B812 /* ElevationViewController.swift */; }; + F1D938FD26BA5555001ECB9A /* ToggleFeatureRequestModesViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = F1C452A226B374C4002FDB34 /* ToggleFeatureRequestModesViewController.swift */; }; F1E4B10A22D66180005FDB4D /* CreateTerrainSurfaceFromLocalRaster.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1E4B10922D66180005FDB4D /* CreateTerrainSurfaceFromLocalRaster.storyboard */; }; F1E4B10C22D6659D005FDB4D /* CreateTerrainSurfaceFromLocalRasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E4B10B22D6659D005FDB4D /* CreateTerrainSurfaceFromLocalRasterViewController.swift */; }; F1E4B10F22D6AF28005FDB4D /* ColormapRenderer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1E4B10E22D6AF28005FDB4D /* ColormapRenderer.storyboard */; }; @@ -941,6 +950,13 @@ dstPath = ""; dstSubfolderSpec = 7; files = ( + F1C092D7272774530000B132 /* VectorTilePackageViewController.swift in CopyFiles */, + F1027F5926F9258E000FA674 /* ExportVectorTilesViewController.swift in CopyFiles */, + 008F292A26E0424C00A5BCD3 /* DisplayMapSwiftUIViewController.swift in CopyFiles */, + 008F292B26E0424C00A5BCD3 /* DisplayMapSwiftUIView.swift in CopyFiles */, + 008F292C26E0424C00A5BCD3 /* SwiftUIMapView.swift in CopyFiles */, + 00CDA5C326F14B9B00DEE7F4 /* SelectENCFeaturesViewController.swift in CopyFiles */, + F1D938FD26BA5555001ECB9A /* ToggleFeatureRequestModesViewController.swift in CopyFiles */, 000FFFB726CDD66B00E3CE7E /* SetUpLocationDrivenGeotriggersViewController.swift in CopyFiles */, 0075033326B4B85700022C7B /* OrbitCameraAroundObjectViewController.swift in CopyFiles */, 0075033426B4B85700022C7B /* OrbitCameraSettingsViewController.swift in CopyFiles */, @@ -1174,9 +1190,6 @@ 3ED0298C1B8E571700ACA70D /* MILUsingURLViewController.swift in CopyFiles */, 3ED0298D1B8E571700ACA70D /* SublayersTableViewController.swift in CopyFiles */, 3ED0298E1B8E571700ACA70D /* SublayerVisibilityViewController.swift in CopyFiles */, - 3ED0298F1B8E571700ACA70D /* OnInteractionCacheViewController.swift in CopyFiles */, - 3ED029901B8E571700ACA70D /* OnInteractionNoCacheViewController.swift in CopyFiles */, - 3ED029911B8E571700ACA70D /* ManualCacheViewController.swift in CopyFiles */, 3ED029921B8E571700ACA70D /* FeatureLayerSelectionViewController.swift in CopyFiles */, 3ED029931B8E571700ACA70D /* FLQueryViewController.swift in CopyFiles */, 3ED029941B8E571700ACA70D /* DefinitionExpressionViewController.swift in CopyFiles */, @@ -1258,6 +1271,9 @@ 003D256D2513F4B6007527C2 /* AddENCExchangeSet.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = AddENCExchangeSet.storyboard; sourceTree = ""; }; 003D256F2513FBB5007527C2 /* ExchangeSetWithoutUpdates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ExchangeSetWithoutUpdates; sourceTree = ""; }; 005507FB25E48134003990D3 /* Redlands.nmea */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Redlands.nmea; sourceTree = ""; }; + 005C713926C32ED10003BDC3 /* DisplayMapSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayMapSwiftUIView.swift; sourceTree = ""; }; + 005C713B26C331470003BDC3 /* DisplayMapSwiftUI.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = DisplayMapSwiftUI.storyboard; sourceTree = ""; }; + 005C713D26C331B90003BDC3 /* DisplayMapSwiftUIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayMapSwiftUIViewController.swift; sourceTree = ""; }; 00686FCF2480889900EDA705 /* NavigateARNavigatorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigateARNavigatorViewController.swift; sourceTree = ""; }; 0069F4BD25F0111D00CA7BD2 /* CreateLoadReport.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CreateLoadReport.storyboard; sourceTree = ""; }; 0069F4C125F0118200CA7BD2 /* CreateLoadReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateLoadReportViewController.swift; sourceTree = ""; }; @@ -1269,6 +1285,7 @@ 007FC9472534E8AE00D8D9A1 /* solarized-dark.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = "solarized-dark.css"; sourceTree = ""; }; 0083AA162628ED18003A28C8 /* ProgressViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ProgressViewController.xib; sourceTree = ""; }; 0083AA172628ED18003A28C8 /* ProgressViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressViewController.swift; sourceTree = ""; }; + 00874F0026DD8C2A009148CF /* SwiftUIMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIMapView.swift; sourceTree = ""; }; 008D175124EEEEBD0001BB8F /* loudoun_anno.geodatabase */ = {isa = PBXFileReference; lastKnownFileType = file; path = loudoun_anno.geodatabase; sourceTree = ""; }; 008D175624EEF3FE0001BB8F /* EditWithBranchVersioningViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditWithBranchVersioningViewController.swift; sourceTree = ""; }; 008D175824EEF4390001BB8F /* EditWithBranchVersioning.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = EditWithBranchVersioning.storyboard; sourceTree = ""; }; @@ -1284,6 +1301,8 @@ 00BA068325311B0D0039EF4D /* hydrography */ = {isa = PBXFileReference; lastKnownFileType = folder; path = hydrography; sourceTree = ""; }; 00C024E42475E4AB00E1DA8D /* NavigateARRoutePlannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigateARRoutePlannerViewController.swift; sourceTree = ""; }; 00C024E62475E54A00E1DA8D /* NavigateAR.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NavigateAR.storyboard; sourceTree = ""; }; + 00CDA5BE26F1289B00DEE7F4 /* SelectENCFeatures.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SelectENCFeatures.storyboard; sourceTree = ""; }; + 00CDA5BF26F1289B00DEE7F4 /* SelectENCFeaturesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectENCFeaturesViewController.swift; sourceTree = ""; }; 00D4E8D9241AA18E002689DB /* ConvexHull.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ConvexHull.storyboard; sourceTree = ""; }; 00D4E8DB241AA18E002689DB /* ConvexHullViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvexHullViewController.swift; sourceTree = ""; }; 00DAE5B22421321300D51281 /* NearestVertex.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NearestVertex.storyboard; sourceTree = ""; }; @@ -1553,12 +1572,6 @@ 3ED028C81B8E3AA500ACA70D /* FLQueryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FLQueryViewController.swift; sourceTree = ""; }; 3ED028CD1B8E3AA500ACA70D /* FeatureLayerSelection.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = FeatureLayerSelection.storyboard; sourceTree = ""; }; 3ED028CE1B8E3AA500ACA70D /* FeatureLayerSelectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureLayerSelectionViewController.swift; sourceTree = ""; }; - 3ED028D41B8E3AA500ACA70D /* OnInteractionCache.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = OnInteractionCache.storyboard; sourceTree = ""; }; - 3ED028D51B8E3AA500ACA70D /* OnInteractionCacheViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnInteractionCacheViewController.swift; sourceTree = ""; }; - 3ED028D81B8E3AA500ACA70D /* ManualCache.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ManualCache.storyboard; sourceTree = ""; }; - 3ED028D91B8E3AA500ACA70D /* ManualCacheViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManualCacheViewController.swift; sourceTree = ""; }; - 3ED028DC1B8E3AA500ACA70D /* OnInteractionNoCache.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = OnInteractionNoCache.storyboard; sourceTree = ""; }; - 3ED028DD1B8E3AA500ACA70D /* OnInteractionNoCacheViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnInteractionNoCacheViewController.swift; sourceTree = ""; }; 3ED028FE1B8E3AB300ACA70D /* AddGraphicsWithRenderer.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = AddGraphicsWithRenderer.storyboard; sourceTree = ""; }; 3ED028FF1B8E3AB300ACA70D /* AddGraphicsWithRendererViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddGraphicsWithRendererViewController.swift; sourceTree = ""; }; 3ED029031B8E3AB300ACA70D /* GOIdentify.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = GOIdentify.storyboard; sourceTree = ""; }; @@ -1792,6 +1805,8 @@ E4B2E26A1FE31EEA00E610FF /* ViewshedGeoElement.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ViewshedGeoElement.storyboard; sourceTree = ""; }; E4C4A4471EE9DD790073B6C0 /* DistanceCompositeSymbol.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DistanceCompositeSymbol.storyboard; sourceTree = ""; }; E4C4A44A1EE9DDDD0073B6C0 /* DistanceCompositeSymbolViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistanceCompositeSymbolViewController.swift; sourceTree = ""; }; + F1027F4D26F91FAA000FA674 /* ExportVectorTilesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportVectorTilesViewController.swift; sourceTree = ""; }; + F1027F5326F92065000FA674 /* ExportVectorTiles.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ExportVectorTiles.storyboard; sourceTree = ""; }; F1034F9922D3EE8900B021DA /* ProjectStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectStackView.swift; sourceTree = ""; }; F1034F9A22D3EE8900B021DA /* Project.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Project.storyboard; sourceTree = ""; }; F1034F9B22D3EE8900B021DA /* ProjectViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectViewController.swift; sourceTree = ""; }; @@ -1825,6 +1840,9 @@ F1987CFE23F7314000DF41CC /* FindClosestFacilityInteractive.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = FindClosestFacilityInteractive.storyboard; sourceTree = ""; }; F1987D0223F7677E00DF41CC /* DisplaySubtypeSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplaySubtypeSettingsViewController.swift; sourceTree = ""; }; F1BCA98E268102EB0027F5A8 /* naperville_imagery.tpkx */ = {isa = PBXFileReference; lastKnownFileType = file; path = naperville_imagery.tpkx; sourceTree = ""; }; + F1C092D3272758830000B132 /* VectorTilePackageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorTilePackageViewController.swift; sourceTree = ""; }; + F1C4529E26B37008002FDB34 /* ToggleFeatureRequestModes.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ToggleFeatureRequestModes.storyboard; sourceTree = ""; }; + F1C452A226B374C4002FDB34 /* ToggleFeatureRequestModesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleFeatureRequestModesViewController.swift; sourceTree = ""; }; F1C81C4422FCEC1C00229CAA /* GetElevationPoint.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = GetElevationPoint.storyboard; sourceTree = ""; }; F1C81C4622FCEC3C00229CAA /* GetElevationPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetElevationPointViewController.swift; sourceTree = ""; }; F1D1E2C02409AB8A006B2801 /* CreateAndSaveKML.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CreateAndSaveKML.storyboard; sourceTree = ""; }; @@ -1962,6 +1980,17 @@ path = "Add ENC exchange set"; sourceTree = ""; }; + 005C713626C32E960003BDC3 /* Display a map SwiftUI */ = { + isa = PBXGroup; + children = ( + 005C713D26C331B90003BDC3 /* DisplayMapSwiftUIViewController.swift */, + 005C713926C32ED10003BDC3 /* DisplayMapSwiftUIView.swift */, + 00874F0026DD8C2A009148CF /* SwiftUIMapView.swift */, + 005C713B26C331470003BDC3 /* DisplayMapSwiftUI.storyboard */, + ); + path = "Display a map SwiftUI"; + sourceTree = ""; + }; 0069F4B825F0110900CA7BD2 /* Create load report */ = { isa = PBXGroup; children = ( @@ -2056,6 +2085,15 @@ path = Archives; sourceTree = ""; }; + 00CDA5BC26F1289B00DEE7F4 /* Select ENC features */ = { + isa = PBXGroup; + children = ( + 00CDA5BF26F1289B00DEE7F4 /* SelectENCFeaturesViewController.swift */, + 00CDA5BE26F1289B00DEE7F4 /* SelectENCFeatures.storyboard */, + ); + path = "Select ENC features"; + sourceTree = ""; + }; 00D4E8D8241AA18E002689DB /* Convex hull */ = { isa = PBXGroup; children = ( @@ -3149,6 +3187,7 @@ 3ED027FE1B8E39DD00ACA70D /* Features */ = { isa = PBXGroup; children = ( + F1C4529D26B36EB0002FDB34 /* Toggle between feature request modes */, 001ECB58248B002B009AC6C0 /* Feature collection layer (portal item) */, F4E5CD7720704369000A6D35 /* Feature layer rendering mode (map) */, C7FF07D321B9D227000D4055 /* Feature layer rendering mode (scene) */, @@ -3156,9 +3195,6 @@ 3E8A257E1D36B6E300D14F08 /* Feature layer (geodatabase) */, D9ED2DA21FD758010043BFE1 /* Feature layer (geopackage) */, D9E2E75020EEAAEE001D0AE0 /* Feature layer (shapefile) */, - 3ED028D31B8E3AA500ACA70D /* Service feature table (cache) */, - 3ED028DB1B8E3AA500ACA70D /* Service feature table (no cache) */, - 3ED028D71B8E3AA500ACA70D /* Service feature table (manual cache) */, 3ED028CC1B8E3AA500ACA70D /* Feature layer selection */, 3ED028C61B8E3AA500ACA70D /* Feature layer query */, 3ED028C01B8E3AA500ACA70D /* Feature layer definition expression */, @@ -3176,6 +3212,8 @@ 3ED027FF1B8E39EE00ACA70D /* Layers */ = { isa = PBXGroup; children = ( + F1027F4C26F91F55000FA674 /* Export vector tiles */, + 00CDA5BC26F1289B00DEE7F4 /* Select ENC features */, 00FFE6D4265DC76A001E92ED /* Query with CQL filters */, 003267B0265C476A00DB4CB5 /* Browse OGC API feature service */, 0022768C26570B7700CC7DB9 /* Display OGC API collection */, @@ -3231,6 +3269,7 @@ isa = PBXGroup; children = ( 3E85263B1B90DD3500690EBD /* Display a map */, + 005C713626C32E960003BDC3 /* Display a map SwiftUI */, 3EAF09431D71086E00F8538F /* Change map view background */, 3E8A258B1D36B97A00D14F08 /* Map loaded */, 3ED028381B8E3A8500ACA70D /* Open map (URL) */, @@ -3454,33 +3493,6 @@ path = "Feature layer selection"; sourceTree = ""; }; - 3ED028D31B8E3AA500ACA70D /* Service feature table (cache) */ = { - isa = PBXGroup; - children = ( - 3ED028D41B8E3AA500ACA70D /* OnInteractionCache.storyboard */, - 3ED028D51B8E3AA500ACA70D /* OnInteractionCacheViewController.swift */, - ); - path = "Service feature table (cache)"; - sourceTree = ""; - }; - 3ED028D71B8E3AA500ACA70D /* Service feature table (manual cache) */ = { - isa = PBXGroup; - children = ( - 3ED028D81B8E3AA500ACA70D /* ManualCache.storyboard */, - 3ED028D91B8E3AA500ACA70D /* ManualCacheViewController.swift */, - ); - path = "Service feature table (manual cache)"; - sourceTree = ""; - }; - 3ED028DB1B8E3AA500ACA70D /* Service feature table (no cache) */ = { - isa = PBXGroup; - children = ( - 3ED028DC1B8E3AA500ACA70D /* OnInteractionNoCache.storyboard */, - 3ED028DD1B8E3AA500ACA70D /* OnInteractionNoCacheViewController.swift */, - ); - path = "Service feature table (no cache)"; - sourceTree = ""; - }; 3ED028FD1B8E3AB300ACA70D /* Add graphics with renderer */ = { isa = PBXGroup; children = ( @@ -4467,6 +4479,16 @@ path = "Distance composite symbol"; sourceTree = ""; }; + F1027F4C26F91F55000FA674 /* Export vector tiles */ = { + isa = PBXGroup; + children = ( + F1027F4D26F91FAA000FA674 /* ExportVectorTilesViewController.swift */, + F1C092D3272758830000B132 /* VectorTilePackageViewController.swift */, + F1027F5326F92065000FA674 /* ExportVectorTiles.storyboard */, + ); + path = "Export vector tiles"; + sourceTree = ""; + }; F1034F9822D3EDFE00B021DA /* Project */ = { isa = PBXGroup; children = ( @@ -4577,6 +4599,15 @@ path = "Find closest facility to an incident (interactive)"; sourceTree = ""; }; + F1C4529D26B36EB0002FDB34 /* Toggle between feature request modes */ = { + isa = PBXGroup; + children = ( + F1C4529E26B37008002FDB34 /* ToggleFeatureRequestModes.storyboard */, + F1C452A226B374C4002FDB34 /* ToggleFeatureRequestModesViewController.swift */, + ); + path = "Toggle between feature request modes"; + sourceTree = ""; + }; F1C81C4322FCEBD100229CAA /* Get elevation at a point */ = { isa = PBXGroup; children = ( @@ -4871,10 +4902,11 @@ 3E03F09B1B05714A0078EB36 /* default.css in Resources */, 007FC9482534E8AE00D8D9A1 /* darkmode.css in Resources */, 3E5E094D1EF850BA00FF3454 /* ManageSublayers.storyboard in Resources */, - 3ED028F71B8E3AA500ACA70D /* ManualCache.storyboard in Resources */, + F1027F5426F92065000FA674 /* ExportVectorTiles.storyboard in Resources */, 21EBBBFD1FE07B0500567F14 /* StatisticalQueryGroupAndSort.storyboard in Resources */, 3ED028641B8E3A8500ACA70D /* GriffithParkJson.txt in Resources */, F1034F9D22D3EE8900B021DA /* Project.storyboard in Resources */, + 005C713C26C331470003BDC3 /* DisplayMapSwiftUI.storyboard in Resources */, 3E0D3AD21AFD1C7500FC60D6 /* ContentPList.plist in Resources */, 3EABC7B91DB183E600C161C6 /* ShastaBW.tif.ovr in Resources */, 3E5E093E1EF4567A00FF3454 /* AddDeleteRelatedFeatures.storyboard in Resources */, @@ -4925,13 +4957,11 @@ 4C4DCA42225EAC3B00832FC5 /* GenerateOfflineMapBasemapByReference.storyboard in Resources */, F1BCA98F268102EB0027F5A8 /* naperville_imagery.tpkx in Resources */, 005507FC25E48134003990D3 /* Redlands.nmea in Resources */, - 3ED028F41B8E3AA500ACA70D /* OnInteractionCache.storyboard in Resources */, F142F7A0267D755700BD608A /* streetmap_SD.tpkx in Resources */, D9E2E76220EEB8FF001D0AE0 /* Public_Art.sbx in Resources */, 3E8A05DD1ECE69840032231C /* Bristol.dae in Resources */, 3E8D54FB1D53E3F000E628D9 /* ScenePropertiesExpressions.storyboard in Resources */, F1E4B10A22D66180005FDB4D /* CreateTerrainSurfaceFromLocalRaster.storyboard in Resources */, - 3ED028FA1B8E3AA500ACA70D /* OnInteractionNoCache.storyboard in Resources */, D97B7E6E1FD9BFE700E1239D /* Subdivisions.sbx in Resources */, C76F757421827C9C00938D8D /* ColorPicker.storyboard in Resources */, D97B7E621FD9BFE700E1239D /* Subdivisions.sbn in Resources */, @@ -4943,6 +4973,7 @@ 3E4BF4461C5AED5100D85919 /* FindAddress.storyboard in Resources */, C7432D3B2187C450009BF1D8 /* ShowLabelsOnLayers.storyboard in Resources */, 00E2762D2425A205000DBEDD /* NavigateRoute.storyboard in Resources */, + 00CDA5C126F1289B00DEE7F4 /* SelectENCFeatures.storyboard in Resources */, 3EC39C431C9B4872003F6459 /* OfflineEditing.storyboard in Resources */, 0023F98125E717AC006B574E /* DisplayDeviceLocationWithNMEADataSources.storyboard in Resources */, 00255DD025156C24001A71DF /* ViewHiddenInfrastructureAR.storyboard in Resources */, @@ -4966,6 +4997,7 @@ C7D0EEDB215EA60800ECDD2C /* DisplayKMLNetworkLinks.storyboard in Resources */, F15694D3265DBE96007AEB61 /* SanDiego_StreetAddress.loc in Resources */, 3E1183DE1ECF5A1000180152 /* Hawaii.csv in Resources */, + F1C4529F26B37008002FDB34 /* ToggleFeatureRequestModes.storyboard in Resources */, F1987D0023F7325300DF41CC /* FindClosestFacilityInteractive.storyboard in Resources */, 3EEA060F1D21C44000E03774 /* DisplayScene.storyboard in Resources */, 3EFDE0F51E36ABB700CBCD92 /* FeatureCollectionLayer.storyboard in Resources */, @@ -5286,6 +5318,7 @@ 008D175724EEF3FE0001BB8F /* EditWithBranchVersioningViewController.swift in Sources */, 4C0CBFFF22F9D38800D3F1DB /* FindClosestFacilityMultipleIncidentsServiceViewController.swift in Sources */, 1682CAEE22404F480039C3E1 /* ViewContentBeneathTerrainSurfaceViewController.swift in Sources */, + 00CDA5C226F1289B00DEE7F4 /* SelectENCFeaturesViewController.swift in Sources */, 3EC39C1B1C9A5928003F6459 /* RouteAroundBarriersViewController.swift in Sources */, 218409DB1FE87E220098BC40 /* GroupByFieldsViewController.swift in Sources */, F15CF0C923EE273A0038F052 /* DisplaySubtypeFeatureLayerViewController.swift in Sources */, @@ -5337,6 +5370,7 @@ 3ED75D071ED4D913009B1F75 /* ServiceAreaSettingsViewController.swift in Sources */, 4CAC91DB2242AB0400663A10 /* LoadWFSWithXMLQueryViewController.swift in Sources */, 166E8F9E1EE62E0E00C57C46 /* RasterLayerViewController.swift in Sources */, + 005C713A26C32ED10003BDC3 /* DisplayMapSwiftUIView.swift in Sources */, C7432D3A2187C450009BF1D8 /* ShowLabelsOnLayersViewController.swift in Sources */, 3E8A25921D36B97A00D14F08 /* MapLoadedViewController.swift in Sources */, F1034FA922D3F5DB00B021DA /* CreateTerrainSurfaceFromLocalTilePackageViewController.swift in Sources */, @@ -5371,18 +5405,19 @@ 3ED0288E1B8E3A8500ACA70D /* SetInitialMapAreaViewController.swift in Sources */, 1218C845225C06AF0027F3E2 /* CameraControllerTableViewController.swift in Sources */, 4C783F3E2252D65A00609B9C /* OpenMobileSceneViewController.swift in Sources */, + 00874F0126DD8C2A009148CF /* SwiftUIMapView.swift in Sources */, 2182C9831FDA01A0003E3A0F /* StatisticalQueryViewController.swift in Sources */, 3E81C1B91D8A0EF700470AA4 /* VectorTileCustomStyleViewController.swift in Sources */, 127D29F6200EC5FC00DD6CDC /* ViewshedSettingsViewController.swift in Sources */, 3EABC7CB1DB1947500C161C6 /* BlendRendererViewController.swift in Sources */, 3E0B58511E36BCCD00951767 /* ViewshedGeoprocessingViewController.swift in Sources */, - 3ED028F51B8E3AA500ACA70D /* OnInteractionCacheViewController.swift in Sources */, 3EEE1D6E1E4935FC003263FC /* GroupUserCell.swift in Sources */, 3EFCE1C41CF374C600E02F67 /* MapPackagesListViewController.swift in Sources */, 3EC39C3F1C9B47C2003F6459 /* OfflineEditingViewController.swift in Sources */, D9EE378C20E2C03100BF8AD1 /* LineOfSightGeoElementViewController.swift in Sources */, 4C5F98C12280A57500414343 /* AuthenticateWithOAuthViewController.swift in Sources */, 3ED028E21B8E3AA500ACA70D /* OverrideRendererViewController.swift in Sources */, + F1C092D4272758830000B132 /* VectorTilePackageViewController.swift in Sources */, C74ECB5021C17E9100129D6D /* DensifyAndGeneralizeViewController.swift in Sources */, 218409DF1FE9A1E00098BC40 /* AddStatisticDefinitionsViewController.swift in Sources */, 3E4BF4561C5BD66C00D85919 /* OfflineGeocodeViewController.swift in Sources */, @@ -5392,8 +5427,8 @@ C7E9D7C721C0500300E6C47E /* BufferOptionsViewController.swift in Sources */, 1259FB931FD295C90037105E /* WMSLayerUsingURLViewController.swift in Sources */, 4C66AAF92107C50B00EE6346 /* QueryMapImageSublayerViewController.swift in Sources */, + F1C452A326B374C4002FDB34 /* ToggleFeatureRequestModesViewController.swift in Sources */, C7237D0E2190F07500A72C76 /* DisplayLocationSettingsViewController.swift in Sources */, - 3ED028F81B8E3AA500ACA70D /* ManualCacheViewController.swift in Sources */, 4C1EF2D2221232C500F73EAE /* IntegratedWindowsAuthenticationMapViewController.swift in Sources */, C7760332217FAC9B0092D978 /* OptionsTableViewController.swift in Sources */, 4CCA30C1223813BF009D2AEF /* IntegratedWindowsAuthenticationPortalMapBrowserViewController.swift in Sources */, @@ -5410,12 +5445,12 @@ 3ED028891B8E3A8500ACA70D /* OpenMapURLViewController.swift in Sources */, 00686FD02480889900EDA705 /* NavigateARNavigatorViewController.swift in Sources */, 3EFDE0F61E36ABB700CBCD92 /* FeatureCollectionLayerViewController.swift in Sources */, - 3ED028FB1B8E3AA500ACA70D /* OnInteractionNoCacheViewController.swift in Sources */, 000FFFB626CB044700E3CE7E /* SetUpLocationDrivenGeotriggersViewController.swift in Sources */, 3E4BF4421C5AE98300D85919 /* FindAddressViewController.swift in Sources */, 092A1C2A22456D9500C5F230 /* DisplayWFSViewController.swift in Sources */, D930E01122EA47F7006C09A1 /* TraceUtilityNetworkViewController.swift in Sources */, 3ED0293A1B8E3AB300ACA70D /* MILShowLegendViewController.swift in Sources */, + 005C713E26C331B90003BDC3 /* DisplayMapSwiftUIViewController.swift in Sources */, 3EEA060E1D21C44000E03774 /* SceneSymbolsViewController.swift in Sources */, 00E276302425A205000DBEDD /* NavigateRouteViewController.swift in Sources */, 3EA15C761ED89EAE00B1F816 /* ChangeSublayerRendererViewController.swift in Sources */, @@ -5504,6 +5539,7 @@ D9E2E75420EEAC5C001D0AE0 /* FeatureLayerShapefileViewController.swift in Sources */, 3E8A05CD1ECE68E80032231C /* CameraSettingsViewController.swift in Sources */, 3EC39C371C9B1ADA003F6459 /* AGSExtensions.swift in Sources */, + F1027F4E26F91FAA000FA674 /* ExportVectorTilesViewController.swift in Sources */, 9DA3CD8C23060687007CB1F3 /* MapSelectionTableViewController.swift in Sources */, 3E3B2C1F1BBCA37F00A99C5E /* ContentCollectionViewController.swift in Sources */, F16922B525158B4400A8F36E /* EditKMLGroundOverlayViewController.swift in Sources */, diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Contents.json b/arcgis-ios-sdk-samples/Assets.xcassets/Contents.json index da4a164c9..73c00596a 100644 --- a/arcgis-ios-sdk-samples/Assets.xcassets/Contents.json +++ b/arcgis-ios-sdk-samples/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/Contents.json b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/Contents.json deleted file mode 100644 index 11c1c53d6..000000000 --- a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/Contents.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "FreehandPolygon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "FreehandPolygon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "FreehandPolygon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon.png b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon.png deleted file mode 100644 index a81050f1e..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon@2x.png b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon@2x.png deleted file mode 100644 index 2976707d7..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon@2x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon@3x.png b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon@3x.png deleted file mode 100644 index 75f5d3c5c..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolygon.imageset/FreehandPolygon@3x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/Contents.json b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/Contents.json deleted file mode 100644 index cf9664885..000000000 --- a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "iOS7_Toolbar_FreehandSketch22.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "iOS7_Toolbar_FreehandSketch44.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/iOS7_Toolbar_FreehandSketch22.png b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/iOS7_Toolbar_FreehandSketch22.png deleted file mode 100644 index ca7ecabee..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/iOS7_Toolbar_FreehandSketch22.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/iOS7_Toolbar_FreehandSketch44.png b/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/iOS7_Toolbar_FreehandSketch44.png deleted file mode 100644 index 00fbd3629..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/FreehandPolyline.imageset/iOS7_Toolbar_FreehandSketch44.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Contents.json b/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Contents.json deleted file mode 100644 index 639f4b5e8..000000000 --- a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Contents.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Point.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Point@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Point@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point.png b/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point.png deleted file mode 100644 index a91c97b60..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point@2x.png b/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point@2x.png deleted file mode 100644 index 79e5d606a..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point@2x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point@3x.png b/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point@3x.png deleted file mode 100644 index 66b9629d7..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Point.imageset/Point@3x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Contents.json b/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Contents.json deleted file mode 100644 index bf2a28210..000000000 --- a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Contents.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Polygon.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Polygon@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Polygon@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon.png b/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon.png deleted file mode 100644 index 3b81d2aea..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon@2x.png b/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon@2x.png deleted file mode 100644 index 41604b59e..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon@2x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon@3x.png b/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon@3x.png deleted file mode 100644 index 6fef22501..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Polygon.imageset/Polygon@3x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Contents.json b/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Contents.json deleted file mode 100644 index 18173f8ef..000000000 --- a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Contents.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Polyline.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "Polyline@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "Polyline@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "template-rendering-intent" : "template" - } -} \ No newline at end of file diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline.png b/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline.png deleted file mode 100644 index cd6eb96b3..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline@2x.png b/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline@2x.png deleted file mode 100644 index 0cb6ec17b..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline@2x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline@3x.png b/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline@3x.png deleted file mode 100644 index 25e67f1c5..000000000 Binary files a/arcgis-ios-sdk-samples/Assets.xcassets/Polyline.imageset/Polyline@3x.png and /dev/null differ diff --git a/arcgis-ios-sdk-samples/Content Display Logic/ContentPList.plist b/arcgis-ios-sdk-samples/Content Display Logic/ContentPList.plist index 49fb84b0b..3b088fdda 100644 --- a/arcgis-ios-sdk-samples/Content Display Logic/ContentPList.plist +++ b/arcgis-ios-sdk-samples/Content Display Logic/ContentPList.plist @@ -15,6 +15,14 @@ descriptionText Display a map with an imagery basemap. + + displayName + Display a map SwiftUI + storyboardName + DisplayMapSwiftUI + descriptionText + Display a map and demonstrate common usecases in SwiftUI. + displayName Change map view background @@ -735,6 +743,27 @@ storyboardName QueryWithCQLFilters + + displayName + Export vector tiles + descriptionText + Export tiles from an online vector tile service. + storyboardName + ExportVectorTiles + + + displayName + Select ENC features + descriptionText + Select features in an ENC layer. + storyboardName + SelectENCFeatures + dependency + + ExchangeSetWithoutUpdates + Hydrography + + @@ -802,30 +831,6 @@ PublicArtShapefile - - displayName - Service feature table (cache) - storyboardName - OnInteractionCache - descriptionText - Display a feature layer from a service using the on interaction cache feature request mode. - - - displayName - Service feature table (no cache) - storyboardName - OnInteractionNoCache - descriptionText - Display a feature layer from a service using the no cache feature request mode. - - - displayName - Service feature table (manual cache) - storyboardName - ManualCache - descriptionText - Display a feature layer from a service using the manual cache feature request mode. - displayName Feature layer selection @@ -918,6 +923,14 @@ descriptionText Query data using a time extent. + + displayName + Toggle between feature request modes + storyboardName + ToggleFeatureRequestModes + descriptionText + Use different feature request modes to populate the map from a service feature table. + diff --git a/arcgis-ios-sdk-samples/Content Display Logic/Controllers/AppInfoViewController.swift b/arcgis-ios-sdk-samples/Content Display Logic/Controllers/AppInfoViewController.swift index 3514cac30..b2597864f 100644 --- a/arcgis-ios-sdk-samples/Content Display Logic/Controllers/AppInfoViewController.swift +++ b/arcgis-ios-sdk-samples/Content Display Logic/Controllers/AppInfoViewController.swift @@ -34,7 +34,20 @@ class AppInfoViewController: UIViewController { // MARK: - Actions + @IBAction func esriCommunityAction() { + UIApplication.shared.open(.esriCommunity) + } + + @IBAction func runtimeSDKForiOSAction() { + UIApplication.shared.open(.developers) + } + @IBAction func closeAction() { self.dismiss(animated: true) } } + +private extension URL { + static let developers = URL(string: "https://developers.arcgis.com/ios/")! + static let esriCommunity = URL(string: "https://community.esri.com/t5/arcgis-runtime-sdk-for-ios-questions/bd-p/arcgis-runtime-sdk-for-ios-questions")! +} diff --git a/arcgis-ios-sdk-samples/Content Display Logic/Controllers/ContentTableViewController.swift b/arcgis-ios-sdk-samples/Content Display Logic/Controllers/ContentTableViewController.swift index e53ad3599..4c99e1ed5 100644 --- a/arcgis-ios-sdk-samples/Content Display Logic/Controllers/ContentTableViewController.swift +++ b/arcgis-ios-sdk-samples/Content Display Logic/Controllers/ContentTableViewController.swift @@ -39,6 +39,11 @@ class ContentTableViewController: UITableViewController { private var downloadProgressObservation: NSKeyValueObservation? + /// Returns the index path for the given sample. + func indexPath(for sample: Sample) -> IndexPath { + IndexPath(row: displayedSamples.firstIndex(of: sample)!, section: 0) + } + override func viewDidLoad() { super.viewDidLoad() @@ -47,6 +52,62 @@ class ContentTableViewController: UITableViewController { downloadProgressView.delegate = self self.downloadProgressView = downloadProgressView } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + selectedSample = nil + } + + // MARK: Sample Selection + + /// The currently selected sample. + private(set) var selectedSample: Sample? { + didSet { + guard selectedSample != oldValue else { return } + selectedSampleDidChange() + } + } + + /// Responds to the selected sample being changed. + private func selectedSampleDidChange() { + if let sample = selectedSample { + let indexPathForSample = indexPath(for: sample) + if tableView.indexPathForSelectedRow != indexPathForSample { + tableView.selectRow(at: indexPathForSample, animated: true, scrollPosition: .top) + } + if !sample.dependencies.isEmpty { + // Download on demand resources. + let bundleResourceRequest = NSBundleResourceRequest(tags: Set(sample.dependencies)) + bundleResourceRequest.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent + self.bundleResourceRequest = bundleResourceRequest + + // Conditionally begin accessing to know if we need to show download progress view or not. + bundleResourceRequest.conditionallyBeginAccessingResources { [weak self] (isResourceAvailable: Bool) in + DispatchQueue.main.async { + // If resource is already available then simply show the sample. + if isResourceAvailable { + self?.showSample(sample) + } + // Else download the resource. + else { + self?.downloadResource(for: sample) + } + } + } + } else { + if let bundleResourceRequest = bundleResourceRequest { + bundleResourceRequest.endAccessingResources() + self.bundleResourceRequest = nil + } + + showSample(sample) + } + } else { + if let indexPath = tableView.indexPathForSelectedRow { + tableView.deselectRow(at: indexPath, animated: true) + } + } + } // MARK: - UITableViewDataSource @@ -70,34 +131,7 @@ class ContentTableViewController: UITableViewController { // hide keyboard if visible view.endEditing(true) - let sample = displayedSamples[indexPath.row] - - // download on demand resources - if !sample.dependencies.isEmpty { - let bundleResourceRequest = NSBundleResourceRequest(tags: Set(sample.dependencies)) - bundleResourceRequest.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent - self.bundleResourceRequest = bundleResourceRequest - - // conditionally begin accessing to know if we need to show download progress view or not - bundleResourceRequest.conditionallyBeginAccessingResources { [weak self] (isResourceAvailable: Bool) in - DispatchQueue.main.async { - // if resource is already available then simply show the sample - if isResourceAvailable { - self?.showSample(sample) - } - // else download the resource - else { - self?.downloadResource(for: sample, at: indexPath) - } - } - } - } else { - // clear bundleResourceRequest - bundleResourceRequest?.endAccessingResources() - - // show view controller - showSample(sample) - } + selectedSample = displayedSamples[indexPath.row] } override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { @@ -106,7 +140,7 @@ class ContentTableViewController: UITableViewController { // MARK: - helpers - private func downloadResource(for sample: Sample, at indexPath: IndexPath) { + private func downloadResource(for sample: Sample) { guard let bundleResourceRequest = bundleResourceRequest else { return } @@ -123,12 +157,9 @@ class ContentTableViewController: UITableViewController { // begin bundleResourceRequest.beginAccessingResources { [weak self] (error: Error?) in - guard let self = self else { - return - } - - // in main thread DispatchQueue.main.async { + guard let self = self else { return } + // remove observation self.downloadProgressObservation = nil @@ -136,9 +167,8 @@ class ContentTableViewController: UITableViewController { self.downloadProgressView?.dismiss() if let error = error { - if let indexPath = self.tableView.indexPathForSelectedRow { - self.tableView.deselectRow(at: indexPath, animated: true) - } + self.bundleResourceRequest = nil + self.selectedSample = nil if (error as NSError).code != NSUserCancelledError { self.presentAlert(message: "Failed to download raster resource :: \(error.localizedDescription)") } @@ -197,7 +227,8 @@ extension ContentTableViewController: DownloadProgressViewDelegate { return } bundleResourceRequest.progress.cancel() - bundleResourceRequest.endAccessingResources() + self.bundleResourceRequest = nil + self.selectedSample = nil } } diff --git a/arcgis-ios-sdk-samples/Content Display Logic/Storyboard/Main.storyboard b/arcgis-ios-sdk-samples/Content Display Logic/Storyboard/Main.storyboard index 55d825fbe..8ed684888 100644 --- a/arcgis-ios-sdk-samples/Content Display Logic/Storyboard/Main.storyboard +++ b/arcgis-ios-sdk-samples/Content Display Logic/Storyboard/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -34,8 +34,29 @@ + + + + + + + - + @@ -84,7 +105,10 @@ - + + + + diff --git a/arcgis-ios-sdk-samples/Display information/Sketch on the map/README.md b/arcgis-ios-sdk-samples/Display information/Sketch on the map/README.md index eed64de1d..24b6bf2cf 100644 --- a/arcgis-ios-sdk-samples/Display information/Sketch on the map/README.md +++ b/arcgis-ios-sdk-samples/Display information/Sketch on the map/README.md @@ -11,9 +11,7 @@ A field worker could annotate features of interest on a map (via the GUI) such a ## How to use the sample -Choose which geometry type to sketch from one of the available buttons. Choose from points, multipoints, polylines, polygons, freehand polylines, and freehand polygons. - -Use the control panel to cancel the sketch, undo or redo changes made to the sketch and to save the sketch to the graphics overlay. There is also the option to select a saved graphic and edit its geometry using the Sketch Editor. The graphics overlay can be cleared using the clear all button. +Tap the add button to choose a geometry for the Sketch Editor. Use the toolbar to undo or redo changes made to the sketch on the graphics overlay. The graphics overlay can be cleared using the clear all button. ## How it works diff --git a/arcgis-ios-sdk-samples/Display information/Sketch on the map/Sketch.storyboard b/arcgis-ios-sdk-samples/Display information/Sketch on the map/Sketch.storyboard index 10f47921c..b697bf7c2 100644 --- a/arcgis-ios-sdk-samples/Display information/Sketch on the map/Sketch.storyboard +++ b/arcgis-ios-sdk-samples/Display information/Sketch on the map/Sketch.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -20,38 +21,34 @@ + - - - - - - - - - - - - - - - - - - + - + + + + + + + - + @@ -63,8 +60,11 @@ + + + @@ -78,11 +78,12 @@ - - + + - - + + + @@ -91,14 +92,9 @@ - - - - - - - - + + + diff --git a/arcgis-ios-sdk-samples/Display information/Sketch on the map/SketchViewController.swift b/arcgis-ios-sdk-samples/Display information/Sketch on the map/SketchViewController.swift index 0096ce241..4d1ee3470 100644 --- a/arcgis-ios-sdk-samples/Display information/Sketch on the map/SketchViewController.swift +++ b/arcgis-ios-sdk-samples/Display information/Sketch on the map/SketchViewController.swift @@ -16,81 +16,107 @@ import UIKit import ArcGIS class SketchViewController: UIViewController { - @IBOutlet private weak var mapView: AGSMapView! - @IBOutlet private weak var geometrySegmentedControl: UISegmentedControl! - @IBOutlet private weak var undoBBI: UIBarButtonItem! - @IBOutlet private weak var redoBBI: UIBarButtonItem! - @IBOutlet private weak var clearBBI: UIBarButtonItem! - - private var sketchEditor: AGSSketchEditor! - - override func viewDidLoad() { - super.viewDidLoad() - - // add the source code button item to the right of navigation bar - (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["SketchViewController"] - - self.sketchEditor = AGSSketchEditor() - self.mapView.sketchEditor = self.sketchEditor - - self.sketchEditor.start(with: nil, creationMode: .polyline) - - self.mapView.map = AGSMap(basemapStyle: .arcGISLightGrayBase) - self.mapView.interactionOptions.isMagnifierEnabled = true - - NotificationCenter.default.addObserver(self, selector: #selector(SketchViewController.respondToGeomChanged), name: .AGSSketchEditorGeometryDidChange, object: nil) - - // set viewpoint - self.mapView.setViewpoint(AGSViewpoint(targetExtent: AGSEnvelope(xMin: -10049589.670344, yMin: 3480099.843772, xMax: -10010071.251113, yMax: 3512023.489701, spatialReference: .webMercator()))) + @IBOutlet var mapView: AGSMapView! { + didSet { + // Add the sketch editor to the map view. + mapView.sketchEditor = sketchEditor + + // Start a default sketch editor with the polyline creation mode. + sketchEditor.start(with: nil, creationMode: .polyline) + + // Set the map. + mapView.map = AGSMap(basemapStyle: .arcGISLightGrayBase) + // Set the viewpoint. + mapView.setViewpoint(AGSViewpoint(targetExtent: AGSEnvelope(xMin: -10049589.670344, yMin: 3480099.843772, xMax: -10010071.251113, yMax: 3512023.489701, spatialReference: .webMercator()))) + } } + @IBOutlet var addBarButtonItem: UIBarButtonItem! + @IBOutlet var undoBarButtonItem: UIBarButtonItem! + @IBOutlet var redoBarButtonItem: UIBarButtonItem! + @IBOutlet var clearBarButtonItem: UIBarButtonItem! + @IBOutlet var statusLabel: UILabel! - @objc - func respondToGeomChanged() { - // Enable/disable UI elements appropriately - self.undoBBI.isEnabled = self.sketchEditor.undoManager.canUndo - self.redoBBI.isEnabled = self.sketchEditor.undoManager.canRedo - self.clearBBI.isEnabled = self.sketchEditor.geometry != nil && !self.sketchEditor.geometry!.isEmpty - } + /// The sketch editor to use on the map. + let sketchEditor = AGSSketchEditor() + /// An observer for the toolbar items. + var barItemObserver: NSObjectProtocol! + /// Key value pairs containing the creation modes and their titles. + let creationModes: KeyValuePairs = [ + "Arrow": AGSSketchCreationMode.arrow, + "Ellipse": .ellipse, + "FreehandPolygon": .freehandPolygon, + "FreehandPolyline": .freehandPolyline, + "Multipoint": .multipoint, + "Point": .point, + "Polygon": .polygon, + "Polyline": .polyline, + "Rectangle": .rectangle, + "Triangle": .triangle + ] // MARK: - Actions - @IBAction func geometryValueChanged(_ segmentedControl: UISegmentedControl) { - switch segmentedControl.selectedSegmentIndex { - case 0:// point - self.sketchEditor.start(with: nil, creationMode: .point) - - case 1:// polyline - self.sketchEditor.start(with: nil, creationMode: .polyline) - - case 2:// freehand polyline - self.sketchEditor.start(with: nil, creationMode: .freehandPolyline) - - case 3:// polygon - self.sketchEditor.start(with: nil, creationMode: .polygon) - - case 4:// freehand polygon - self.sketchEditor.start(with: nil, creationMode: .freehandPolygon) - - default: - break + @IBAction func addGeometryButtonTapped(_ sender: UIBarButtonItem) { + // Create an alert controller for the action sheets. + let alertController = UIAlertController(title: "Select a creation mode", message: nil, preferredStyle: .actionSheet) + // Create an action for each creation mode and add it to the alert controller. + creationModes.forEach { name, mode in + let action = UIAlertAction(title: name, style: .default) { [weak self] _ in + self?.statusLabel.text = "\(name) selected." + self?.sketchEditor.start(with: nil, creationMode: mode) + } + alertController.addAction(action) } + // Add "cancel" item. + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) + alertController.addAction(cancelAction) - self.mapView.sketchEditor = self.sketchEditor + // Present the action sheets when the add button is tapped. + alertController.popoverPresentationController?.barButtonItem = addBarButtonItem + present(alertController, animated: true) } @IBAction func undo() { - if self.sketchEditor.undoManager.canUndo { // extra check, just to be sure - self.sketchEditor.undoManager.undo() - } + // Check if there are actions to undo. + guard sketchEditor.undoManager.canUndo else { return } + sketchEditor.undoManager.undo() } @IBAction func redo() { - if self.sketchEditor.undoManager.canRedo { // extra check, just to be sure - self.sketchEditor.undoManager.redo() - } + // Check if there are actions to redo. + guard sketchEditor.undoManager.canRedo else { return } + sketchEditor.undoManager.redo() } @IBAction func clear() { self.sketchEditor.clearGeometry() } + + // MARK: - Views + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + // Add an observer to udate UI when needed. + barItemObserver = NotificationCenter.default.addObserver(forName: .AGSSketchEditorGeometryDidChange, object: sketchEditor, queue: nil, using: { [unowned self] _ in + // Enable/disable UI elements appropriately. + undoBarButtonItem.isEnabled = sketchEditor.undoManager.canUndo + redoBarButtonItem.isEnabled = sketchEditor.undoManager.canRedo + clearBarButtonItem.isEnabled = sketchEditor.geometry.map { !$0.isEmpty } ?? false + }) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if let observer = barItemObserver { + // Remove the observer. + NotificationCenter.default.removeObserver(observer) + } + } + + override func viewDidLoad() { + super.viewDidLoad() + // Add the source code button item to the right of navigation bar. + (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["SketchViewController"] + } } diff --git a/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-1.png b/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-1.png index 3b47d54da..502965dd9 100644 Binary files a/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-1.png and b/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-1.png differ diff --git a/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-2.png b/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-2.png index 7c6510297..1ec0e1442 100644 Binary files a/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-2.png and b/arcgis-ios-sdk-samples/Display information/Sketch on the map/sketch-on-map-2.png differ diff --git a/arcgis-ios-sdk-samples/Features/Service feature table (cache)/OnInteractionCacheViewController.swift b/arcgis-ios-sdk-samples/Features/Service feature table (cache)/OnInteractionCacheViewController.swift deleted file mode 100644 index bcc744c9b..000000000 --- a/arcgis-ios-sdk-samples/Features/Service feature table (cache)/OnInteractionCacheViewController.swift +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2016 Esri. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import UIKit -import ArcGIS - -class OnInteractionCacheViewController: UIViewController { - @IBOutlet private weak var mapView: AGSMapView! - - private let featureServiceURL = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/PoolPermits/FeatureServer/0" - - override func viewDidLoad() { - super.viewDidLoad() - - // add the source code button item to the right of navigation bar - (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["OnInteractionCacheViewController"] - - // initialize map with light gray canvas basemap - let map = AGSMap(basemapStyle: .arcGISLightGrayBase) - - // feature layer - let featureTable = AGSServiceFeatureTable(url: URL(string: featureServiceURL)!) - // set the request mode - featureTable.featureRequestMode = AGSFeatureRequestMode.onInteractionCache - let featureLayer = AGSFeatureLayer(featureTable: featureTable) - // add the feature layer to the map - map.operationalLayers.add(featureLayer) - - mapView.map = map - let extent = AGSEnvelope( - xMin: -1.30758164047166E7, - yMin: 4014771.46954516, - xMax: -1.30730056797177E7, - yMax: 4016869.78617381, - spatialReference: .webMercator() - ) - mapView.setViewpoint(AGSViewpoint(targetExtent: extent)) - } -} diff --git a/arcgis-ios-sdk-samples/Features/Service feature table (manual cache)/ManualCache.storyboard b/arcgis-ios-sdk-samples/Features/Service feature table (manual cache)/ManualCache.storyboard deleted file mode 100644 index af7353bde..000000000 --- a/arcgis-ios-sdk-samples/Features/Service feature table (manual cache)/ManualCache.storyboard +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/arcgis-ios-sdk-samples/Features/Service feature table (manual cache)/ManualCacheViewController.swift b/arcgis-ios-sdk-samples/Features/Service feature table (manual cache)/ManualCacheViewController.swift deleted file mode 100644 index 81efd8240..000000000 --- a/arcgis-ios-sdk-samples/Features/Service feature table (manual cache)/ManualCacheViewController.swift +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 Esri. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import UIKit -import ArcGIS - -class ManualCacheViewController: UIViewController { - @IBOutlet private weak var mapView: AGSMapView! - - var featureTable: AGSServiceFeatureTable! - - override func viewDidLoad() { - super.viewDidLoad() - - // add the source code button item to the right of navigation bar - (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["ManualCacheViewController"] - - // initialize map with topographic basemap - let map = AGSMap(basemapStyle: .arcGISTopographic) - - // create feature table using a url - self.featureTable = AGSServiceFeatureTable(url: URL(string: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SF311/FeatureServer/0")!) - // set the feature request mode to Manual Cache - featureTable.featureRequestMode = AGSFeatureRequestMode.manualCache - // create feature layer using this feature table - let featureLayer = AGSFeatureLayer(featureTable: self.featureTable) - // add feature layer to the map - map.operationalLayers.add(featureLayer) - - // assign map to the map view - mapView.map = map - mapView.setViewpoint(AGSViewpoint(center: AGSPoint(x: -13630484, y: 4545415, spatialReference: .webMercator()), scale: 500000)) - } - - // MARK: - Actions - - @IBAction func populateAction(_ sender: AnyObject) { - // set query parameters - let params = AGSQueryParameters() - // for specific request type - params.whereClause = "req_Type = 'Tree Maintenance or Damage'" - - // populate features based on query - self.featureTable.populateFromService(with: params, clearCache: true, outFields: ["*"]) { [weak self] (result: AGSFeatureQueryResult?, error: Error?) in - // check for error - if let error = error { - self?.presentAlert(error: error) - } else { - // the resulting features should be displayed on the map - // you can print the count of features - print("Populated \(result?.featureEnumerator().allObjects.count ?? 0) features.") - } - } - } -} diff --git a/arcgis-ios-sdk-samples/Features/Service feature table (no cache)/OnInteractionNoCache.storyboard b/arcgis-ios-sdk-samples/Features/Service feature table (no cache)/OnInteractionNoCache.storyboard deleted file mode 100644 index c74a1d08e..000000000 --- a/arcgis-ios-sdk-samples/Features/Service feature table (no cache)/OnInteractionNoCache.storyboard +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/arcgis-ios-sdk-samples/Features/Service feature table (no cache)/OnInteractionNoCacheViewController.swift b/arcgis-ios-sdk-samples/Features/Service feature table (no cache)/OnInteractionNoCacheViewController.swift deleted file mode 100644 index 13a273224..000000000 --- a/arcgis-ios-sdk-samples/Features/Service feature table (no cache)/OnInteractionNoCacheViewController.swift +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2016 Esri. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import UIKit -import ArcGIS - -class OnInteractionNoCacheViewController: UIViewController { - @IBOutlet private weak var mapView: AGSMapView! - - private let featureServiceURL = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/PoolPermits/FeatureServer/0" - - override func viewDidLoad() { - super.viewDidLoad() - - // add the source code button item to the right of navigation bar - (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["OnInteractionNoCacheViewController"] - - // initialize map with topographic basemap - let map = AGSMap(basemapStyle: .arcGISTopographic) - - // feature layer - let featureTable = AGSServiceFeatureTable(url: URL(string: featureServiceURL)!) - // set the request mode - featureTable.featureRequestMode = AGSFeatureRequestMode.onInteractionNoCache - let featureLayer = AGSFeatureLayer(featureTable: featureTable) - // add the feature layer to the map - map.operationalLayers.add(featureLayer) - - mapView.map = map - let extent = AGSEnvelope( - xMin: -1.30758164047166E7, - yMin: 4014771.46954516, - xMax: -1.30730056797177E7, - yMax: 4016869.78617381, - spatialReference: .webMercator() - ) - mapView.setViewpoint(AGSViewpoint(targetExtent: extent)) - } -} diff --git a/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/README.md b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/README.md new file mode 100644 index 000000000..e66092f98 --- /dev/null +++ b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/README.md @@ -0,0 +1,45 @@ +# Toggle between feature request modes + +Use different feature request modes to populate the map from a service feature table. + +![Toggle between feature request modes](toggle-feature-request-modes.png) + +## Use case + +Feature tables can be initialized with a feature request mode which controls how frequently features are requested and locally cached in response to panning, zooming, selecting, or querying. The feature request mode affects performance and should be chosen based on considerations such as how often the data is expected to change or how often changes in the data should be reflected to the user. + +* `OnInteractionCache` - fetches features within the current extent when needed (after a pan or zoom action) from the server and caches those features in a table on the client. Queries will be performed locally if the features are present, otherwise they will be requested from the server. This mode minimizes requests to the server and is useful for large batches of features which will change infrequently. +* `OnInteractionNoCache` - always fetches features from the server and doesn't cache any features on the client. This mode is best for features that may change often on the server or whose changes need to always be visible. + +> **NOTE**: **No cache** does not guarantee that features won't be cached locally. Feature request mode is a performance concept unrelated to data security. + +* `ManualCache` - only fetches features when explicitly populated from a query. This mode is best for features that change minimally or when it is not critical for the user to see the latest changes. + +## How to use the sample + +Choose a request mode by tapping the "Mode" button. Pan and zoom to see how the features update at different scales. If you choose `Manual cache`, tap the "Populate" button to manually get a cache with a subset of features (where "Condition < 4") within the extent. + +Note: The service limits requests to 1000 features. + +## How it works + +1. Create an `AGSServiceFeatureTable` with a feature service URL. +2. Set the `featureRequestMode` property of the `AGSServiceFeatureTable` to the desired mode (Cache, No cache, or Manual cache) before the table is loaded. + * If using `Manual cache`, populate the features with `AGSServiceFeatureTable.populateFromService(with:clearCache:outFields:completion:)`. +3. Create an `AGSFeatureLayer` with the feature table and add it to a map's operational layers to display it. + +## Relevant API + +* AGSFeatureLayer +* AGSFeatureRequestMode +* AGSServiceFeatureTable +* AGSServiceFeatureTable.featureRequestMode +* AGSServiceFeatureTable.populateFromService(with:clearCache:outFields:completion:) + +## About the data + +This sample uses the [Trees of Portland](https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/Trees_of_Portland/FeatureServer/0) service showcasing over 200,000 street trees in Portland, OR. Each tree point models the health of the tree (green - better, red - worse) as well as the diameter of its trunk. + +## Tags + +cache, data, feature, feature request mode, performance diff --git a/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/README.metadata.json b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/README.metadata.json new file mode 100644 index 000000000..060efde81 --- /dev/null +++ b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/README.metadata.json @@ -0,0 +1,32 @@ +{ + "category": "Features", + "description": "Use different feature request modes to populate the map from a service feature table.", + "ignore": false, + "images": [ + "toggle-feature-request-modes.png" + ], + "keywords": [ + "cache", + "data", + "feature", + "feature request mode", + "performance", + "AGSFeatureLayer", + "AGSFeatureRequestMode", + "AGSServiceFeatureTable", + "AGSServiceFeatureTable.featureRequestMode", + "AGSServiceFeatureTable.populateFromService(with:clearCache:outFields:completion:)" + ], + "redirect_from": [], + "relevant_apis": [ + "AGSFeatureLayer", + "AGSFeatureRequestMode", + "AGSServiceFeatureTable", + "AGSServiceFeatureTable.featureRequestMode", + "AGSServiceFeatureTable.populateFromService(with:clearCache:outFields:completion:)" + ], + "snippets": [ + "ToggleFeatureRequestModesViewController.swift" + ], + "title": "Toggle between feature request modes" +} diff --git a/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/ToggleFeatureRequestModes.storyboard b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/ToggleFeatureRequestModes.storyboard new file mode 100644 index 000000000..4a420d223 --- /dev/null +++ b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/ToggleFeatureRequestModes.storyboard @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/ToggleFeatureRequestModesViewController.swift b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/ToggleFeatureRequestModesViewController.swift new file mode 100644 index 000000000..e8f47d589 --- /dev/null +++ b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/ToggleFeatureRequestModesViewController.swift @@ -0,0 +1,134 @@ +// Copyright 2021 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import ArcGIS + +class ToggleFeatureRequestModesViewController: UIViewController { + @IBOutlet var mapView: AGSMapView! { + didSet { + mapView.map = AGSMap(basemapStyle: .arcGISTopographic) + mapView.setViewpoint(AGSViewpoint(latitude: 45.5266, longitude: -122.6219, scale: 6000)) + } + } + @IBOutlet var modeBarButtonItem: UIBarButtonItem! + @IBOutlet var populateBarButtonItem: UIBarButtonItem! + @IBOutlet var statusLabel: UILabel! + + private static let featureServiceURL = URL(string: "https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/Trees_of_Portland/FeatureServer/0")! + private let featureTable = AGSServiceFeatureTable(url: featureServiceURL) + + private enum FeatureRequestMode: CaseIterable { + case cache, noCache, manualCache + + var title: String { + switch self { + case .cache: + return "Cache" + case .noCache: + return "No cache" + case .manualCache: + return "Manual cache" + } + } + + var mode: AGSFeatureRequestMode { + switch self { + case .cache: + return .onInteractionCache + case .noCache: + return .onInteractionNoCache + case .manualCache: + return .manualCache + } + } + } + + /// Prompt mode selection. + @IBAction private func modeButtonTapped(_ button: UIBarButtonItem) { + // Set up action sheets. + let alertController = UIAlertController( + title: "Choose a feature request mode.", + message: nil, + preferredStyle: .actionSheet + ) + // Create an action for each mode. + FeatureRequestMode.allCases.forEach { mode in + let action = UIAlertAction(title: mode.title, style: .default) { [self] _ in + changeFeatureRequestMode(to: mode.mode) + let message = "\(mode.title) enabled." + setStatus(message: message) + } + alertController.addAction(action) + } + // Add a cancel action. + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) + alertController.addAction(cancelAction) + alertController.popoverPresentationController?.barButtonItem = modeBarButtonItem + present(alertController, animated: true) + } + + /// Populate for manual cache mode. + @IBAction private func populateManualCache(_ button: UIBarButtonItem) { + // Set query parameters. + let params = AGSQueryParameters() + // Query for all tree conditions except "dead" with coded value '4'. + params.whereClause = "Condition < '4'" + params.geometry = mapView.visibleArea?.extent + // Show the progress HUD. + UIApplication.shared.showProgressHUD(message: "Populating") + // Populate features based on the query. + featureTable.populateFromService(with: params, clearCache: true, outFields: ["*"]) { [weak self] (result: AGSFeatureQueryResult?, error: Error?) in + guard let self = self else { return } + // Hide progress HUD. + UIApplication.shared.hideProgressHUD() + if let error = error { + self.presentAlert(error: error) + } else { + // Display the number of features found. + let message = "Populated \(result?.featureEnumerator().allObjects.count ?? 0) features." + self.setStatus(message: message) + } + } + } + + /// Set the appropriate feature request mode. + private func changeFeatureRequestMode(to mode: AGSFeatureRequestMode) { + // Enable or disable the populate bar button item when appropriate. + if mode == .manualCache { + populateBarButtonItem.isEnabled = true + } else { + populateBarButtonItem.isEnabled = false + } + let map = mapView.map! + map.operationalLayers.removeAllObjects() + // Set the request mode. + featureTable.featureRequestMode = mode + let featureLayer = AGSFeatureLayer(featureTable: featureTable) + // Add the feature layer to the map. + map.operationalLayers.add(featureLayer) + } + + /// Set the status. + private func setStatus(message: String) { + statusLabel.text = message + } + + override func viewDidLoad() { + super.viewDidLoad() + setStatus(message: "Select a feature request mode.") + // Add the source code button item to the right of navigation bar. + (self.navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["ToggleFeatureRequestModesViewController"] + } +} diff --git a/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/toggle-feature-request-modes.png b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/toggle-feature-request-modes.png new file mode 100644 index 000000000..b620e1d6d Binary files /dev/null and b/arcgis-ios-sdk-samples/Features/Toggle between feature request modes/toggle-feature-request-modes.png differ diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/ExportVectorTiles.storyboard b/arcgis-ios-sdk-samples/Layers/Export vector tiles/ExportVectorTiles.storyboard new file mode 100644 index 000000000..08bf7e869 --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Export vector tiles/ExportVectorTiles.storyboard @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/ExportVectorTilesViewController.swift b/arcgis-ios-sdk-samples/Layers/Export vector tiles/ExportVectorTilesViewController.swift new file mode 100644 index 000000000..c192e5e48 --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Export vector tiles/ExportVectorTilesViewController.swift @@ -0,0 +1,240 @@ +// Copyright 2021 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import ArcGIS + +class ExportVectorTilesViewController: UIViewController { + // MARK: Storyboard views + + /// The map view managed by the view controller. + @IBOutlet var mapView: AGSMapView! { + didSet { + mapView.map = AGSMap(basemap: AGSBasemap(style: .arcGISStreetsNight)) + // Set the viewpoint. + mapView.setViewpoint(AGSViewpoint(latitude: 34.049, longitude: -117.181, scale: 1e4)) + } + } + + /// A view to emphasize the extent of exported tile layer. + @IBOutlet var extentView: UIView! { + didSet { + extentView.layer.borderColor = UIColor.red.cgColor + extentView.layer.borderWidth = 2 + } + } + + /// A bar button to initiate the download task. + @IBOutlet var exportVectorTilesButton: UIBarButtonItem! + @IBOutlet var progressView: UIProgressView! + @IBOutlet var progressLabel: UILabel! + @IBOutlet var progressParentView: UIView! + @IBOutlet var cancelButton: UIButton! + + // MARK: Properties + + /// The resulting vector tiled layer. + var vectorTiledLayer: AGSArcGISVectorTiledLayer? + /// The extent of the map that is to be exported. + var extent: AGSEnvelope? + /// The export task to request the tile package with the same URL as the tile layer. + var exportVectorTilesTask: AGSExportVectorTilesTask? + /// An export job to download the tile package. + var job: AGSExportVectorTilesJob? { + didSet { + // Remove key-value observation. + progressObservation = nil + exportVectorTilesButton.isEnabled = job == nil + // Refresh the progress view according to the job status. + updateProgressViewUI() + // Observe the job's progress. + progressView.observedProgress = job?.progress + // Observe the localized description in order to update the text label. + progressObservation = job?.progress.observe(\.localizedDescription, options: .initial) { [weak self] progress, _ in + DispatchQueue.main.async { + // Update the progress label. + self?.progressLabel.text = progress.localizedDescription + } + } + } + } + + /// A URL to the temporary directory to temporarily store the exported vector tile package. + let vtpkTemporaryURL: URL + /// A URL to the temporary directory to temporarily store the style item resources. + let styleTemporaryURL: URL + /// A directory to temporarily store all items. + let temporaryDirectory: URL + + /// Observation to track the export vector tiles job. + private var progressObservation: NSKeyValueObservation? + + required init?(coder: NSCoder) { + temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(ProcessInfo().globallyUniqueString) + try? FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: false) + vtpkTemporaryURL = temporaryDirectory + .appendingPathComponent("myTileCache", isDirectory: false) + .appendingPathExtension("vtpk") + styleTemporaryURL = temporaryDirectory + .appendingPathComponent("styleItemResources", isDirectory: true) + super.init(coder: coder) + } + + // MARK: Methods + + /// Initiate the `AGSExportVectorTilesTask` to download a tile package. + /// - Parameters: + /// - exportTask: An `AGSExportVectorTilesTask` to run the export job. + /// - vectorTileCacheURL: A URL to where the tile package should be saved. + func initiateDownload(exportTask: AGSExportVectorTilesTask, vectorTileCacheURL: URL) { + // Set the max scale parameter to 10% of the map's scale to limit the + // number of tiles exported to within the vector tiled layer's max tile export limit. + let maxScale = mapView.mapScale * 0.1 + // Get current area of interest marked by the extent view. + let areaOfInterest = envelope(for: extentView) + // Get the parameters by specifying the selected area and vector tiled layer's max scale as maxScale. + exportTask.defaultExportVectorTilesParameters(withAreaOfInterest: areaOfInterest, maxScale: maxScale) { [weak self] parameters, error in + guard let self = self, let exportVectorTilesTask = self.exportVectorTilesTask else { return } + if let params = parameters { + // Start exporting the tiles with the resulting parameters. + self.exportVectorTiles(exportTask: exportVectorTilesTask, parameters: params, vectorTileCacheURL: vectorTileCacheURL) + } else if let error = error { + self.presentAlert(error: error) + } + } + } + + /// Export vector tiles with the `AGSExportVectorTilesJob` from the export task. + /// - Parameters: + /// - exportTask: An `AGSExportVectorTilesTask` to run the export job. + /// - parameters: The parameters of the export task. + /// - vectorTileCacheURL: A URL to where the tile package is saved. + func exportVectorTiles(exportTask: AGSExportVectorTilesTask, parameters: AGSExportVectorTilesParameters, vectorTileCacheURL: URL) { + // Create the job with the parameters and download URLs. + let job = exportTask.exportVectorTilesJob(with: parameters, vectorTileCacheDownloadFileURL: vtpkTemporaryURL, itemResourceCacheDownloadDirectory: styleTemporaryURL) + self.job = job + // Start the job. + job.start(statusHandler: nil) { [weak self] (result, error) in + guard let self = self else { return } + self.job = nil + if let result = result, + let tileCache = result.vectorTileCache, + let itemResourceCache = result.itemResourceCache { + self.updateProgressViewUI() + // Create the vector tiled layer with the tile cache and item resource cache. + self.vectorTiledLayer = AGSArcGISVectorTiledLayer(vectorTileCache: tileCache, itemResourceCache: itemResourceCache) + // Set the extent. + self.extent = parameters.areaOfInterest as? AGSEnvelope + self.performSegue(withIdentifier: "showResult", sender: nil) + } else if let error = error { + let nsError = error as NSError + if !(nsError.domain == NSCocoaErrorDomain && nsError.code == NSUserCancelledError) { + self.presentAlert(error: error) + } + } + } + } + + /// Get the extent within the extent view for generating a vector tile package. + func envelope(for view: UIView) -> AGSEnvelope { + let frame = mapView.convert(view.frame, from: self.view) + + let minPoint = mapView.screen(toLocation: CGPoint(x: frame.minX, y: frame.minY)) + let maxPoint = mapView.screen(toLocation: CGPoint(x: frame.maxX, y: frame.maxY)) + let extent = AGSEnvelope(min: minPoint, max: maxPoint) + return extent + } + + /// Update the progress view accordingly. + func updateProgressViewUI() { + let isJobNil = job == nil + mapView.interactionOptions.isEnabled = isJobNil + progressParentView.isHidden = isJobNil + } + + /// Remove temporary files that are created for each job. + func removeTemporaryFiles() { + try? FileManager.default.removeItem(at: vtpkTemporaryURL) + try? FileManager.default.removeItem(at: styleTemporaryURL) + } + + // MARK: Actions + + @IBAction func exportTilesBarButtonTapped(_ sender: UIBarButtonItem) { + if let exportVectorTilesTask = exportVectorTilesTask, + let vectorTileSourceInfo = exportVectorTilesTask.vectorTileSourceInfo, + vectorTileSourceInfo.exportTilesAllowed { + // Try to download when exporting tiles is allowed. + initiateDownload(exportTask: exportVectorTilesTask, vectorTileCacheURL: vtpkTemporaryURL) + } else { + presentAlert(title: "Error", message: "Exporting tiles is not supported for the service.") + } + } + + @IBAction func cancelAction() { + // Cancel export vector tiles job and remove the temporary files. + job?.progress.cancel() + removeTemporaryFiles() + } + + // MARK: - Navigation + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let navController = segue.destination as? UINavigationController, + let rootController = navController.viewControllers.first as? VectorTilePackageViewController { + // Display the view controller as a formsheet - specified for iPads. + rootController.modalPresentationStyle = .formSheet + rootController.isModalInPresentation = true + rootController.tiledLayerResult = vectorTiledLayer + rootController.extent = extent + rootController.delegate = self + } + } + + // MARK: UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + mapView.map?.load { [weak self] _ in + guard let self = self else { return } + // Obtain the vector tiled layer and its URL from the baselayers. + guard let vectorTiledLayer = self.mapView.map?.basemap.baseLayers.firstObject as? AGSArcGISVectorTiledLayer, + let vectorTiledLayerURL = vectorTiledLayer.url else { return } + // The export task to request the tile package with the same URL as the tile layer. + let exportVectorTilesTask = AGSExportVectorTilesTask(url: vectorTiledLayerURL) + self.exportVectorTilesTask = exportVectorTilesTask + exportVectorTilesTask.load { [weak self] error in + guard let self = self else { return } + if let error = error { + self.presentAlert(error: error) + } else { + self.exportVectorTilesButton.isEnabled = true + } + } + } + // Add the source code button item to the right of navigation bar. + (navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["ExportVectorTilesViewController", "VectorTilePackageViewController"] + } + + deinit { + try? FileManager.default.removeItem(at: temporaryDirectory) + } +} + +extension ExportVectorTilesViewController: VectorTilePackageViewControllerDelegate { + func vectorTilePackageViewControllerDidFinish(_ controller: VectorTilePackageViewController) { + // Remove the downloaded vector tile package files after finishing viewing them. + removeTemporaryFiles() + } +} diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/README.md b/arcgis-ios-sdk-samples/Layers/Export vector tiles/README.md new file mode 100644 index 000000000..8b6bd8f33 --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Export vector tiles/README.md @@ -0,0 +1,41 @@ +# Export vector tiles + +Export tiles from an online vector tile service. + +![Exporting vector tiles](export-vector-tiles-1.png) +![Successful export vector tiles](export-vector-tiles-2.png) + +## Use case + +Field workers with limited network connectivity can use exported vector tiles as a basemap for use while offline. + +## How to use the sample + +When the vector tiled layer loads, zoom in to the extent you want to export. The red box shows the extent that will be exported. Tap the "Export vector tiles" button to start exporting the vector tiles. An error will show if the extent is larger than the maximum limit allowed. When finished, a new map view will show the exported result. + +## How it works + +1. Create an `AGSArcGISVectorTiledLayer`, from the map's base layers. +2. Create an `AGSExportVectorTilesTask` using the vector tiled layer's URL. +3. Create default `AGSExportVectorTilesParameters` from the task, specifying extent and maximum scale. +4. Create an `AGSExportVectorTilesJob` from the task using the parameters, specifying a vector tile cache path, and an item resource path. The resource path is required if you want to export the tiles with the style. +5. Start the job, and once it completes successfully, get the resulting `AGSExportVectorTilesResult`. +6. Get the `AGSVectorTileCache` and `AGSItemResourceCache` from the result to create an `AGSArcGISVectorTiledLayer` that can be displayed to the map view. + +## Relevant API + +* AGSArcGISVectorTiledLayer +* AGSExportVectorTilesJob +* AGSExportVectorTilesParameters +* AGSExportVectorTilesResult +* AGSExportVectorTilesTask +* AGSItemResourceCache +* AGSVectorTileCache + +## Additional information + +Vector tiles have high drawing performance and smaller file size compared to regular tiled layers, due to consisting solely of points, lines, and polygons. However, in ArcGIS Runtime SDK they cannot be displayed in scenes. Visit [ArcGIS for Developers](https://developers.arcgis.com/ios/layers/#layer-types) to learn more about the characteristics of ArcGIS vector tiled layers. + +## Tags + +cache, download, offline, vector diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/README.metadata.json b/arcgis-ios-sdk-samples/Layers/Export vector tiles/README.metadata.json new file mode 100644 index 000000000..9f926c2ec --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Export vector tiles/README.metadata.json @@ -0,0 +1,37 @@ +{ + "category": "Layers", + "description": "Export tiles from an online vector tile service.", + "ignore": false, + "images": [ + "export-vector-tiles-1.png", + "export-vector-tiles-2.png" + ], + "keywords": [ + "cache", + "download", + "offline", + "vector", + "AGSArcGISVectorTiledLayer", + "AGSExportVectorTilesJob", + "AGSExportVectorTilesParameters", + "AGSExportVectorTilesResult", + "AGSExportVectorTilesTask", + "AGSItemResourceCache", + "AGSVectorTileCache" + ], + "redirect_from": [], + "relevant_apis": [ + "AGSArcGISVectorTiledLayer", + "AGSExportVectorTilesJob", + "AGSExportVectorTilesParameters", + "AGSExportVectorTilesResult", + "AGSExportVectorTilesTask", + "AGSItemResourceCache", + "AGSVectorTileCache" + ], + "snippets": [ + "ExportVectorTilesViewController.swift", + "VectorTilePackageViewController.swift" + ], + "title": "Export vector tiles" +} diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/VectorTilePackageViewController.swift b/arcgis-ios-sdk-samples/Layers/Export vector tiles/VectorTilePackageViewController.swift new file mode 100644 index 000000000..b56a43997 --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Export vector tiles/VectorTilePackageViewController.swift @@ -0,0 +1,49 @@ +// Copyright 2021 Esri. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import ArcGIS + +protocol VectorTilePackageViewControllerDelegate: AnyObject { + /// Tell the delegate that the user finished viewing the vector tile package. + /// - Parameter controller: The controller sending the message. + func vectorTilePackageViewControllerDidFinish(_ controller: VectorTilePackageViewController) +} + +class VectorTilePackageViewController: UIViewController { + @IBOutlet var resultView: AGSMapView! + /// The resulting vector tiled layer. + var tiledLayerResult: AGSArcGISVectorTiledLayer? + /// The extent of the vector tiled layer. + var extent: AGSEnvelope? + /// A delegate to notify other view controllers. + weak var delegate: VectorTilePackageViewControllerDelegate? + + @IBAction func doneAction(_ sender: UIBarButtonItem) { + delegate?.vectorTilePackageViewControllerDidFinish(self) + dismiss(animated: true) + } + + override func viewDidLoad() { + super.viewDidLoad() + guard + let tiledLayerResult = tiledLayerResult, + let extent = extent + else { return } + // Set the map in the resulting view to the exported vector tile package. + resultView.map = AGSMap(basemap: AGSBasemap(baseLayer: tiledLayerResult)) + // Set the viewpoint to be the extent. + resultView.setViewpoint(AGSViewpoint(targetExtent: extent)) + } +} diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/export-vector-tiles-1.png b/arcgis-ios-sdk-samples/Layers/Export vector tiles/export-vector-tiles-1.png new file mode 100644 index 000000000..d84341d9d Binary files /dev/null and b/arcgis-ios-sdk-samples/Layers/Export vector tiles/export-vector-tiles-1.png differ diff --git a/arcgis-ios-sdk-samples/Layers/Export vector tiles/export-vector-tiles-2.png b/arcgis-ios-sdk-samples/Layers/Export vector tiles/export-vector-tiles-2.png new file mode 100644 index 000000000..74b331362 Binary files /dev/null and b/arcgis-ios-sdk-samples/Layers/Export vector tiles/export-vector-tiles-2.png differ diff --git a/arcgis-ios-sdk-samples/Layers/Identify raster cell/IdentifyRasterCellViewController.swift b/arcgis-ios-sdk-samples/Layers/Identify raster cell/IdentifyRasterCellViewController.swift index d7ea71304..125e69e41 100644 --- a/arcgis-ios-sdk-samples/Layers/Identify raster cell/IdentifyRasterCellViewController.swift +++ b/arcgis-ios-sdk-samples/Layers/Identify raster cell/IdentifyRasterCellViewController.swift @@ -35,7 +35,11 @@ class IdentifyRasterCellViewController: UIViewController { // MARK: Properties /// The raster layer created using local raster file. - let rasterLayer = AGSRasterLayer(raster: AGSRaster(name: "SA_EVI_8Day_03May20", extension: "tif")) + let rasterLayer = AGSRasterLayer(raster: AGSRaster(fileURL: Bundle.main.url( + forResource: "SA_EVI_8Day_03May20", + withExtension: "tif", + subdirectory: "SA_EVI_8Day_03May20" + )!)) /// A formatter for coordinates. let formatter: NumberFormatter = { diff --git a/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/OpenStreetMapLayerViewController.swift b/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/OpenStreetMapLayerViewController.swift index 238795f46..b551d4dcb 100644 --- a/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/OpenStreetMapLayerViewController.swift +++ b/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/OpenStreetMapLayerViewController.swift @@ -16,19 +16,27 @@ import UIKit import ArcGIS class OpenStreetMapLayerViewController: UIViewController { - @IBOutlet private weak var mapView: AGSMapView! + @IBOutlet var mapView: AGSMapView! { + didSet { + // Assign the map to the map view. + mapView.map = makeMap() + mapView.setViewpoint(AGSViewpoint(latitude: 34.056295, longitude: -117.195800, scale: 577790.554289)) + } + } + + /// Create a map. + /// - Returns: An `AGSMap` object. + func makeMap() -> AGSMap { + // Create an OpenStreetMap layer that requests tiles from its servers. + let openStreetMapLayer = AGSOpenStreetMapLayer() + // Initialize a map and set the OpenStreetMap layer as its basemap. + let map = AGSMap(basemap: AGSBasemap(baseLayer: openStreetMapLayer)) + return map + } override func viewDidLoad() { super.viewDidLoad() - - // initialize map with an OpenStreetMap standard basemap - let map = AGSMap(basemapStyle: .osmStandard) - - // assign the map to the map view - mapView.map = map - mapView.setViewpoint(AGSViewpoint(latitude: 34.056295, longitude: -117.195800, scale: 577790.554289)) - - // add the source code button item to the right of navigation bar - (navigationItem.rightBarButtonItem as! SourceCodeBarButtonItem).filenames = ["OpenStreetMapLayerViewController"] + // Add the source code button item to the right of navigation bar. + (navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = ["OpenStreetMapLayerViewController"] } } diff --git a/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/README.md b/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/README.md index c0238567b..9bfa0453a 100644 --- a/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/README.md +++ b/arcgis-ios-sdk-samples/Layers/OpenStreetMap layer/README.md @@ -10,12 +10,13 @@ You may want to create a map with an [OpenStreetMap](https://www.openstreetmap.o ## How to use the sample -When the sample opens, it will automatically display the map with the OpenStreetMap basemap. Pan and zoom to observe the basemap. +The OpenStreetMap basemap will display upon launch. Pan and zoom to explore the base layer. ## How it works -1. Create an `AGSMap` with `osmStandard` as the `basemapStyle` and specify the other properties. -2. Apply the `AGSMap` to the `AGSMapView`. +1. Create an instance of `AGSOpenStreetMapLayer`. +2. Create an instance of `AGSMap` using the OpenStreetMap layer as the base layer. +3. Set the map to the map view's `map` property. ## Relevant API diff --git a/arcgis-ios-sdk-samples/Layers/Select ENC features/README.md b/arcgis-ios-sdk-samples/Layers/Select ENC features/README.md new file mode 100644 index 000000000..606551989 --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Select ENC features/README.md @@ -0,0 +1,43 @@ +# Select ENC features + +Select features in an ENC layer. + +![Image of select ENC features](select-enc-features.png) + +## Use case + +You can use selection to identify a feature and learn more about it. ENC layers often have many overlapping features and features of mixed geometry in a single layer, so the sample includes code for identifying the most relevant feature. + +## How to use the sample + +Tap to select ENC features. Feature acronym and description will be displayed in a callout. + +## How it works + +1. Load and display the ENC layers. +2. Identify the layers tapped on the map view with `AGSGeoView.identifyLayers(atScreenPoint:tolerance:returnPopupsOnly:completion:)`. +3. Filter the list of identified layers to include only results where the `layerContent` is an `AGSENCLayer`. +4. Get the first result. +5. Get the first feature in the result. +6. Select that feature by calling `AGSENCLayer.select(_:)`. +7. Show the feature's acronym and description in a callout. + +## Relevant API + +* AGSENCFeature +* AGSENCLayer +* AGSIdentifyLayerResult + +## Offline data + +This sample downloads the [ENC Exchange Set without updates](https://www.arcgis.com/home/item.html?id=9d2987a825c646468b3ce7512fb76e2d) item from *ArcGIS Online*. + +The latest [hydrography package](https://developers.arcgis.com/downloads/data) can be downloaded from *ArcGIS Developer* website (login is required). The *S57DataDictionary.xml* file is contained in it along with many others but a user does not need to know that in order to render ENC data. + +## Additional information + +Read more about [Electronic Navigational Charts](https://developers.arcgis.com/ios/layers/display-electronic-navigational-charts/) and its [deployment](https://developers.arcgis.com/ios/license-and-deployment/deployment/#enc-electronic-navigational-charts-style-directory) on the *ArcGIS Developer* website. + +## Tags + +chart, hydrography, identify, IHO, maritime, S-57, S57, select, selection diff --git a/arcgis-ios-sdk-samples/Layers/Select ENC features/README.metadata.json b/arcgis-ios-sdk-samples/Layers/Select ENC features/README.metadata.json new file mode 100644 index 000000000..7ef8da7cf --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Select ENC features/README.metadata.json @@ -0,0 +1,32 @@ +{ + "category": "Layers", + "description": "Select features in an ENC layer.", + "ignore": false, + "images": [ + "select-enc-features.png" + ], + "keywords": [ + "IHO", + "S-57", + "S57", + "chart", + "hydrography", + "identify", + "maritime", + "select", + "selection", + "AGSENCFeature", + "AGSENCLayer", + "AGSIdentifyLayerResult" + ], + "redirect_from": [], + "relevant_apis": [ + "AGSENCFeature", + "AGSENCLayer", + "AGSIdentifyLayerResult" + ], + "snippets": [ + "SelectENCFeaturesViewController.swift" + ], + "title": "Select ENC features" +} diff --git a/arcgis-ios-sdk-samples/Features/Service feature table (cache)/OnInteractionCache.storyboard b/arcgis-ios-sdk-samples/Layers/Select ENC features/SelectENCFeatures.storyboard similarity index 52% rename from arcgis-ios-sdk-samples/Features/Service feature table (cache)/OnInteractionCache.storyboard rename to arcgis-ios-sdk-samples/Layers/Select ENC features/SelectENCFeatures.storyboard index 632363767..c43d2055e 100644 --- a/arcgis-ios-sdk-samples/Features/Service feature table (cache)/OnInteractionCache.storyboard +++ b/arcgis-ios-sdk-samples/Layers/Select ENC features/SelectENCFeatures.storyboard @@ -1,48 +1,42 @@ - + - + - - + + - - + + - - + + - + - - - - - + + + + - - - - - - + - + - + diff --git a/arcgis-ios-sdk-samples/Layers/Select ENC features/SelectENCFeaturesViewController.swift b/arcgis-ios-sdk-samples/Layers/Select ENC features/SelectENCFeaturesViewController.swift new file mode 100644 index 000000000..d53e686a6 --- /dev/null +++ b/arcgis-ios-sdk-samples/Layers/Select ENC features/SelectENCFeaturesViewController.swift @@ -0,0 +1,140 @@ +// Copyright 2021 Esri +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import ArcGIS + +class SelectENCFeaturesViewController: UIViewController { + /// The map view managed by the view controller. + @IBOutlet var mapView: AGSMapView! { + didSet { + mapView.map = AGSMap(basemapStyle: .arcGISOceans) + mapView.setViewpoint(AGSViewpoint(latitude: -32.5, longitude: 60.95, scale: 1e5)) + mapView.touchDelegate = self + mapView.callout.isAccessoryButtonHidden = true + } + } + + /// The ENC layer that contains the current selected feature. + var currentENCLayer: AGSENCLayer? + /// A reference to the cancelable identify layer operation. + var identifyOperation: AGSCancelable? + + /// A URL to the temporary SENC data directory. + let temporaryURL: URL = { + let directoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(ProcessInfo().globallyUniqueString) + // Create and return the full, unique URL to the temporary folder. + try? FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true) + return directoryURL + }() + + /// Load the ENC dataset and add it to the map view. + func addENCExchangeSet() { + // Load catalog file in ENC exchange set from bundle. + let catalogURL = Bundle.main.url( + forResource: "CATALOG", + withExtension: "031", + subdirectory: "ExchangeSetWithoutUpdates/ENC_ROOT" + )! + let encExchangeSet = AGSENCExchangeSet(fileURLs: [catalogURL]) + + // URL to the "hydrography" data folder that contains the "S57DataDictionary.xml" file. + let hydrographyDirectory = Bundle.main.url( + forResource: "S57DataDictionary", + withExtension: "xml", + subdirectory: "hydrography" + )! + .deletingLastPathComponent() + // Set environment settings for loading the dataset. + let environmentSettings = AGSENCEnvironmentSettings.shared() + environmentSettings.resourceDirectory = hydrographyDirectory + // The SENC data directory is for temporarily storing generated files. + environmentSettings.sencDataDirectory = temporaryURL + // Update the display settings to make the chart less cluttered. + updateDisplaySettings() + + encExchangeSet.load { [weak self] error in + guard let self = self else { return } + if let error = error { + self.presentAlert(error: error) + } else { + // Create a list of ENC layers from datasets. + let encLayers = encExchangeSet.datasets.map { AGSENCLayer(cell: AGSENCCell(dataset: $0)) } + // Add layers to the map. + self.mapView.map?.operationalLayers.addObjects(from: encLayers) + } + } + } + + /// Update the display settings to make the chart less cluttered. + func updateDisplaySettings() { + let displaySettings = AGSENCEnvironmentSettings.shared().displaySettings + + let textGroupVisibilitySettings = displaySettings.textGroupVisibilitySettings + textGroupVisibilitySettings.geographicNames = false + textGroupVisibilitySettings.natureOfSeabed = false + + let viewingGroupSettings = displaySettings.viewingGroupSettings + viewingGroupSettings.buoysBeaconsAidsToNavigation = false + viewingGroupSettings.depthContours = false + viewingGroupSettings.spotSoundings = false + } + + override func viewDidLoad() { + super.viewDidLoad() + // Add the source code button item to the right of navigation bar. + (navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = ["SelectENCFeaturesViewController"] + addENCExchangeSet() + } + + deinit { + // Recursively remove all files in the sample-specific + // temporary folder and the folder itself. + try? FileManager.default.removeItem(at: temporaryURL) + // Reset ENC environment display settings. + let displaySettings = AGSENCEnvironmentSettings.shared().displaySettings + displaySettings.textGroupVisibilitySettings.resetToDefaults() + displaySettings.viewingGroupSettings.resetToDefaults() + } +} + +// MARK: - AGSGeoViewTouchDelegate + +extension SelectENCFeaturesViewController: AGSGeoViewTouchDelegate { + func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) { + // Dismiss any presenting callout. + mapView.callout.dismiss() + // Clear selection before identifying layers. + currentENCLayer?.clearSelection() + // Clear in-progress identify operation. + identifyOperation?.cancel() + + // Identify the tapped feature. + identifyOperation = mapView.identifyLayers(atScreenPoint: screenPoint, tolerance: 10, returnPopupsOnly: false) { [weak self] identifyResults, _ in + guard let self = self else { return } + self.identifyOperation = nil + guard let results = identifyResults, + let firstResult = results.first(where: { $0.layerContent is AGSENCLayer }), + let containingLayer = firstResult.layerContent as? AGSENCLayer, + let firstFeature = firstResult.geoElements.first as? AGSENCFeature else { + return + } + self.currentENCLayer = containingLayer + containingLayer.select(firstFeature) + self.mapView.callout.title = firstFeature.acronym + self.mapView.callout.detail = firstFeature.featureDescription + self.mapView.callout.show(at: mapPoint, screenOffset: .zero, rotateOffsetWithMap: false, animated: true) + } + } +} diff --git a/arcgis-ios-sdk-samples/Layers/Select ENC features/select-enc-features.png b/arcgis-ios-sdk-samples/Layers/Select ENC features/select-enc-features.png new file mode 100644 index 000000000..c9419541e Binary files /dev/null and b/arcgis-ios-sdk-samples/Layers/Select ENC features/select-enc-features.png differ diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUI.storyboard b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUI.storyboard new file mode 100644 index 000000000..78d6e6243 --- /dev/null +++ b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUI.storyboard @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUIView.swift b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUIView.swift new file mode 100644 index 000000000..678e55b12 --- /dev/null +++ b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUIView.swift @@ -0,0 +1,89 @@ +// Copyright 2021 Esri +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import ArcGIS + +struct DisplayMapSwiftUIView: View { + struct MapOption: Equatable { + let title: String + let map: AGSMap + + static let topographic = MapOption(title: "Topographic", map: AGSMap(basemapStyle: .arcGISTopographic)) + static let oceans = MapOption(title: "Oceans", map: AGSMap(basemapStyle: .arcGISOceans)) + static let navigation = MapOption(title: "Navigation", map: AGSMap(basemapStyle: .arcGISNavigation)) + + static let allOptions: [MapOption] = [.topographic, .oceans, .navigation] + } + + /// The graphics overlay used to initialize `SwiftUIMapView`. + let graphicsOverlay: AGSGraphicsOverlay = { + let overlay = AGSGraphicsOverlay() + overlay.renderer = AGSSimpleRenderer( + symbol: AGSSimpleMarkerSymbol(style: .circle, color: .red, size: 10) + ) + return overlay + }() + + @State private var clearGraphicsButtonDisabled = true + @State private var selectedMapOption: MapOption = .topographic + /// A Boolean that indicates whether the action sheet is presented. + @State private var showingOptions = false + + var body: some View { + VStack { + SwiftUIMapView(map: selectedMapOption.map, graphicsOverlays: [graphicsOverlay]) + .onSingleTap { _, mapPoint in + let graphic = AGSGraphic(geometry: mapPoint, symbol: nil) + graphicsOverlay.graphics.add(graphic) + updateClearGraphicsButtonState() + } + .edgesIgnoringSafeArea(.all) + HStack { + Button("Choose Map", action: { showingOptions = true }) + .actionSheet(isPresented: $showingOptions) { + ActionSheet( + title: Text("Choose a basemap."), + buttons: MapOption.allOptions.map { option in + .default(Text(option.title), action: { + guard option != selectedMapOption else { return } + clearGraphics() + selectedMapOption = option + }) + } + [.cancel()] + ) + } + Spacer() + Button("Clear Graphics", action: clearGraphics) + .disabled(clearGraphicsButtonDisabled) + } + .padding() + } + } + + func clearGraphics() { + graphicsOverlay.graphics.removeAllObjects() + updateClearGraphicsButtonState() + } + + func updateClearGraphicsButtonState() { + clearGraphicsButtonDisabled = (graphicsOverlay.graphics as! [AGSGraphic]).isEmpty + } +} + +struct DisplayMapSwiftUIView_Previews: PreviewProvider { + static var previews: some View { + DisplayMapSwiftUIView() + } +} diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUIViewController.swift b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUIViewController.swift new file mode 100644 index 000000000..cd266823e --- /dev/null +++ b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/DisplayMapSwiftUIViewController.swift @@ -0,0 +1,38 @@ +// Copyright 2021 Esri +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import SwiftUI + +class DisplayMapSwiftUIViewController: UIHostingController { + required init?(coder: NSCoder) { + // `UIHostingController` integrates SwiftUI views into a UIKit + // view hierarchy. At creation time, specify the SwiftUI as the + // root view for this view controller. + super.init(coder: coder, rootView: DisplayMapSwiftUIView()) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // Add the source code button item to the right of navigation bar. + if let sourceCodeBarButtonItem = navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem, + sourceCodeBarButtonItem.filenames.isEmpty { + sourceCodeBarButtonItem.filenames = [ + "SwiftUIMapView", + "DisplayMapSwiftUIView", + "DisplayMapSwiftUIViewController" + ] + } + } +} diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/README.md b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/README.md new file mode 100644 index 000000000..76d5397ba --- /dev/null +++ b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/README.md @@ -0,0 +1,39 @@ +# Display a map SwiftUI + +Display a map and demonstrate common usecases in SwiftUI. + +![Image of Display map SwiftUI](display-map-swiftui.png) + +## Use case + +The map is the fundamental building block of any GIS application and is used to specify how geographic data is organized and communicated to your users. With the introduction of SwiftUI in iOS 13, it's worth exploring the interoperability of SwiftUI and current UIKit-based Runtime SDK, while waiting for the new Runtime SDK in Swift. + +## How to use the sample + +Run the sample to view the map. Single tap to add a circle marker to the map. Tap "Choose Map" button to change the map. Tap "Clear Graphics" button to clear all added graphics. + +## How it works + +1. Create a `SwiftUIMapView` to wrap an `AGSMapView` with SwiftUI `UIViewRepresentable` protocol. +2. Create a nested `Coordinator` type to implement `AGSGeoViewTouchDelegate` methods. +3. Initialize the `SwiftUIMapView` with a map and graphics overlays. +4. Create a `DisplayMapSwiftUIView` to combine the `SwiftUIMapView` together with other view components, automatically preview in Xcode, and provide useful context for the map. +5. Create a `UIHostingController` to integrate and manage the `DisplayMapSwiftUIView` into UIKit view hierarchy. + +## Relevant API + +* AGSBasemapStyle +* AGSMap +* AGSMapView + +## Additional information + +This sample demonstrates how to use `AGSMapView` in SwiftUI. It features the following: + +* Using SwiftUI together with storyboard via [`UIHostingController`](https://developer.apple.com/documentation/swiftui/uihostingcontroller) +* Embedding a `UIView` in a SwiftUI view via [`UIViewRepresentable`](https://developer.apple.com/documentation/swiftui/uiviewrepresentable) protocol, and using `Coordinator` to translate Cocoa delegate methods into SwiftUI view actions +* Common usecases of a map: adding graphics to a map view; changing the map displayed by a map view; responding to tap events on a map view + +## Tags + +basemap style, interface, interoperability, map, SwiftUI diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/README.metadata.json b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/README.metadata.json new file mode 100644 index 000000000..b13a3b4ea --- /dev/null +++ b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/README.metadata.json @@ -0,0 +1,30 @@ +{ + "category": "Maps", + "description": "Display a map and demonstrate common usecases in SwiftUI.", + "ignore": false, + "images": [ + "display-map-swiftui.png" + ], + "keywords": [ + "SwiftUI", + "basemap style", + "interface", + "interoperability", + "map", + "AGSBasemapStyle", + "AGSMap", + "AGSMapView" + ], + "redirect_from": [], + "relevant_apis": [ + "AGSBasemapStyle", + "AGSMap", + "AGSMapView" + ], + "snippets": [ + "SwiftUIMapView.swift", + "DisplayMapSwiftUIView.swift", + "DisplayMapSwiftUIViewController.swift" + ], + "title": "Display a map SwiftUI" +} diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/SwiftUIMapView.swift b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/SwiftUIMapView.swift new file mode 100644 index 000000000..e9c6f8703 --- /dev/null +++ b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/SwiftUIMapView.swift @@ -0,0 +1,87 @@ +// Copyright 2021 Esri +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import ArcGIS + +struct SwiftUIMapView { + let map: AGSMap + let graphicsOverlays: [AGSGraphicsOverlay] + + init( + map: AGSMap, + graphicsOverlays: [AGSGraphicsOverlay] = [] + ) { + self.map = map + self.graphicsOverlays = graphicsOverlays + } + + private var onSingleTapAction: ((CGPoint, AGSPoint) -> Void)? +} + +extension SwiftUIMapView { + /// Sets a closure to perform when a single tap occurs on the map view. + /// - Parameter action: The closure to perform upon single tap. + func onSingleTap(perform action: @escaping (CGPoint, AGSPoint) -> Void) -> Self { + var copy = self + copy.onSingleTapAction = action + return copy + } +} + +extension SwiftUIMapView: UIViewRepresentable { + typealias UIViewType = AGSMapView + + func makeCoordinator() -> Coordinator { + Coordinator( + onSingleTapAction: onSingleTapAction + ) + } + + func makeUIView(context: Context) -> AGSMapView { + let uiView = AGSMapView() + uiView.map = map + uiView.graphicsOverlays.setArray(graphicsOverlays) + uiView.touchDelegate = context.coordinator + return uiView + } + + func updateUIView(_ uiView: AGSMapView, context: Context) { + if map != uiView.map { + uiView.map = map + } + if graphicsOverlays != uiView.graphicsOverlays as? [AGSGraphicsOverlay] { + uiView.graphicsOverlays.setArray(graphicsOverlays) + } + context.coordinator.onSingleTapAction = onSingleTapAction + } +} + +extension SwiftUIMapView { + class Coordinator: NSObject { + var onSingleTapAction: ((CGPoint, AGSPoint) -> Void)? + + init( + onSingleTapAction: ((CGPoint, AGSPoint) -> Void)? + ) { + self.onSingleTapAction = onSingleTapAction + } + } +} + +extension SwiftUIMapView.Coordinator: AGSGeoViewTouchDelegate { + func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) { + onSingleTapAction?(screenPoint, mapPoint) + } +} diff --git a/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/display-map-swiftui.png b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/display-map-swiftui.png new file mode 100644 index 000000000..4bc71d039 Binary files /dev/null and b/arcgis-ios-sdk-samples/Maps/Display a map SwiftUI/display-map-swiftui.png differ diff --git a/arcgis-ios-sdk-samples/Maps/Map reference scale/MapReferenceScaleSettingsViewController.swift b/arcgis-ios-sdk-samples/Maps/Map reference scale/MapReferenceScaleSettingsViewController.swift index 053766b42..4213f9da0 100644 --- a/arcgis-ios-sdk-samples/Maps/Map reference scale/MapReferenceScaleSettingsViewController.swift +++ b/arcgis-ios-sdk-samples/Maps/Map reference scale/MapReferenceScaleSettingsViewController.swift @@ -89,8 +89,6 @@ class MapReferenceScaleSettingsViewController: UITableViewController { /// The observer of the reference scale of the map. private var referenceScaleObserver: NSObjectProtocol? - /// The observer of the scale of the map. - private var scaleObserver: NSObjectProtocol? // MARK: UIViewController